forked from vergnet/application-amicale
Improve about components to match linter
This commit is contained in:
parent
3e4f2f4ac1
commit
483970c9a8
3 changed files with 601 additions and 532 deletions
|
@ -1,36 +1,31 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import packageJson from '../../../package';
|
|
||||||
import {List} from 'react-native-paper';
|
import {List} from 'react-native-paper';
|
||||||
import {StackNavigationProp} from "@react-navigation/stack";
|
import {View} from 'react-native-animatable';
|
||||||
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
import {View} from "react-native-animatable";
|
import packageJson from '../../../package.json';
|
||||||
|
|
||||||
type listItem = {
|
type ListItemType = {
|
||||||
name: string,
|
name: string,
|
||||||
version: string
|
version: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the dependencies list from the raw json
|
* Generates the dependencies list from the raw json
|
||||||
*
|
*
|
||||||
* @param object The raw json
|
* @param object The raw json
|
||||||
* @return {Array<listItem>}
|
* @return {Array<ListItemType>}
|
||||||
*/
|
*/
|
||||||
function generateListFromObject(object: { [key: string]: string }): Array<listItem> {
|
function generateListFromObject(object: {
|
||||||
let list = [];
|
[key: string]: string,
|
||||||
let keys = Object.keys(object);
|
}): Array<ListItemType> {
|
||||||
let values = Object.values(object);
|
const list = [];
|
||||||
for (let i = 0; i < keys.length; i++) {
|
const keys = Object.keys(object);
|
||||||
list.push({name: keys[i], version: values[i]});
|
keys.forEach((key: string) => {
|
||||||
}
|
list.push({name: key, version: object[key]});
|
||||||
//$FlowFixMe
|
});
|
||||||
return list;
|
return list;
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
navigation: StackNavigationProp,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const LIST_ITEM_HEIGHT = 64;
|
const LIST_ITEM_HEIGHT = 64;
|
||||||
|
@ -38,38 +33,45 @@ const LIST_ITEM_HEIGHT = 64;
|
||||||
/**
|
/**
|
||||||
* Class defining a screen showing the list of libraries used by the app, taken from package.json
|
* Class defining a screen showing the list of libraries used by the app, taken from package.json
|
||||||
*/
|
*/
|
||||||
export default class AboutDependenciesScreen extends React.Component<Props> {
|
export default class AboutDependenciesScreen extends React.Component<null> {
|
||||||
|
data: Array<ListItemType>;
|
||||||
|
|
||||||
data: Array<listItem>;
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.data = generateListFromObject(packageJson.dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
keyExtractor = (item: ListItemType): string => item.name;
|
||||||
super();
|
|
||||||
this.data = generateListFromObject(packageJson.dependencies);
|
|
||||||
}
|
|
||||||
|
|
||||||
keyExtractor = (item: listItem) => item.name;
|
getRenderItem = ({item}: {item: ListItemType}): React.Node => (
|
||||||
|
<List.Item
|
||||||
|
title={item.name}
|
||||||
|
description={item.version.replace('^', '').replace('~', '')}
|
||||||
|
style={{height: LIST_ITEM_HEIGHT}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
renderItem = ({item}: { item: listItem }) =>
|
getItemLayout = (
|
||||||
<List.Item
|
data: ListItemType,
|
||||||
title={item.name}
|
index: number,
|
||||||
description={item.version.replace('^', '').replace('~', '')}
|
): {length: number, offset: number, index: number} => ({
|
||||||
style={{height: LIST_ITEM_HEIGHT}}
|
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(): React.Node {
|
||||||
|
return (
|
||||||
render() {
|
<View>
|
||||||
return (
|
<CollapsibleFlatList
|
||||||
<View>
|
data={this.data}
|
||||||
<CollapsibleFlatList
|
keyExtractor={this.keyExtractor}
|
||||||
data={this.data}
|
renderItem={this.getRenderItem}
|
||||||
keyExtractor={this.keyExtractor}
|
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
|
||||||
renderItem={this.renderItem}
|
removeClippedSubviews
|
||||||
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
|
getItemLayout={this.getItemLayout}
|
||||||
removeClippedSubviews={true}
|
/>
|
||||||
getItemLayout={this.itemLayout}
|
</View>
|
||||||
/>
|
);
|
||||||
</View>
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,351 +1,390 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {FlatList, Linking, Platform, View} from 'react-native';
|
import {FlatList, Linking, Platform} 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 {StackNavigationProp} from '@react-navigation/stack';
|
||||||
import {StackNavigationProp} from "@react-navigation/stack";
|
import packageJson from '../../../package.json';
|
||||||
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
|
import APP_LOGO from '../../../assets/android.icon.png';
|
||||||
|
|
||||||
type ListItem = {
|
type ListItemType = {
|
||||||
onPressCallback: () => void,
|
onPressCallback: () => void,
|
||||||
icon: string,
|
icon: string,
|
||||||
text: string,
|
text: string,
|
||||||
showChevron: boolean
|
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',
|
||||||
playstore: 'https://play.google.com/store/apps/details?id=fr.amicaleinsat.application',
|
playstore:
|
||||||
git: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/README.md',
|
'https://play.google.com/store/apps/details?id=fr.amicaleinsat.application',
|
||||||
changelog: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md',
|
git:
|
||||||
license: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE',
|
'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/README.md',
|
||||||
authorMail: "mailto:vergnet@etud.insa-toulouse.fr?" +
|
changelog:
|
||||||
"subject=" +
|
'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md',
|
||||||
"Application Amicale INSA Toulouse" +
|
license:
|
||||||
"&body=" +
|
'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE',
|
||||||
"Coucou !\n\n",
|
authorMail:
|
||||||
authorLinkedin: 'https://www.linkedin.com/in/arnaud-vergnet-434ba5179/',
|
'mailto:vergnet@etud.insa-toulouse.fr?' +
|
||||||
yohanMail: "mailto:ysimard@etud.insa-toulouse.fr?" +
|
'subject=' +
|
||||||
"subject=" +
|
'Application Amicale INSA Toulouse' +
|
||||||
"Application Amicale INSA Toulouse" +
|
'&body=' +
|
||||||
"&body=" +
|
'Coucou !\n\n',
|
||||||
"Coucou !\n\n",
|
authorLinkedin: 'https://www.linkedin.com/in/arnaud-vergnet-434ba5179/',
|
||||||
yohanLinkedin: 'https://www.linkedin.com/in/yohan-simard',
|
yohanMail:
|
||||||
react: 'https://facebook.github.io/react-native/',
|
'mailto:ysimard@etud.insa-toulouse.fr?' +
|
||||||
meme: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
'subject=' +
|
||||||
|
'Application Amicale INSA Toulouse' +
|
||||||
|
'&body=' +
|
||||||
|
'Coucou !\n\n',
|
||||||
|
yohanLinkedin: 'https://www.linkedin.com/in/yohan-simard',
|
||||||
|
react: 'https://facebook.github.io/react-native/',
|
||||||
|
meme: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type PropsType = {
|
||||||
navigation: StackNavigationProp,
|
navigation: StackNavigationProp,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a link in the device's browser
|
* Opens a link in the device's browser
|
||||||
* @param link The link to open
|
* @param link The link to open
|
||||||
*/
|
*/
|
||||||
function openWebLink(link) {
|
function openWebLink(link: string) {
|
||||||
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
|
Linking.openURL(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining an about screen. This screen shows the user information about the app and it's author.
|
* Class defining an about screen. This screen shows the user information about the app and it's author.
|
||||||
*/
|
*/
|
||||||
class AboutScreen extends React.Component<Props> {
|
class AboutScreen extends React.Component<PropsType> {
|
||||||
|
/**
|
||||||
|
* Data to be displayed in the app card
|
||||||
|
*/
|
||||||
|
appData = [
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
openWebLink(Platform.OS === 'ios' ? links.appstore : links.playstore);
|
||||||
|
},
|
||||||
|
icon: Platform.OS === 'ios' ? 'apple' : 'google-play',
|
||||||
|
text:
|
||||||
|
Platform.OS === 'ios'
|
||||||
|
? i18n.t('screens.about.appstore')
|
||||||
|
: i18n.t('screens.about.playstore'),
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
const {navigation} = this.props;
|
||||||
|
navigation.navigate('feedback');
|
||||||
|
},
|
||||||
|
icon: 'bug',
|
||||||
|
text: i18n.t('screens.feedback.homeButtonTitle'),
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
openWebLink(links.git);
|
||||||
|
},
|
||||||
|
icon: 'git',
|
||||||
|
text: 'Git',
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
openWebLink(links.changelog);
|
||||||
|
},
|
||||||
|
icon: 'refresh',
|
||||||
|
text: i18n.t('screens.about.changelog'),
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
openWebLink(links.license);
|
||||||
|
},
|
||||||
|
icon: 'file-document',
|
||||||
|
text: i18n.t('screens.about.license'),
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data to be displayed in the app card
|
* Data to be displayed in the author card
|
||||||
*/
|
*/
|
||||||
appData = [
|
authorData = [
|
||||||
{
|
{
|
||||||
onPressCallback: () => openWebLink(Platform.OS === "ios" ? links.appstore : links.playstore),
|
onPressCallback: () => {
|
||||||
icon: Platform.OS === "ios" ? 'apple' : 'google-play',
|
openWebLink(links.meme);
|
||||||
text: Platform.OS === "ios" ? i18n.t('screens.about.appstore') : i18n.t('screens.about.playstore'),
|
},
|
||||||
showChevron: true
|
icon: 'account-circle',
|
||||||
},
|
text: 'Arnaud VERGNET',
|
||||||
{
|
showChevron: false,
|
||||||
onPressCallback: () => this.props.navigation.navigate("feedback"),
|
},
|
||||||
icon: 'bug',
|
{
|
||||||
text: i18n.t("screens.feedback.homeButtonTitle"),
|
onPressCallback: () => {
|
||||||
showChevron: true
|
openWebLink(links.authorMail);
|
||||||
},
|
},
|
||||||
{
|
icon: 'email',
|
||||||
onPressCallback: () => openWebLink(links.git),
|
text: i18n.t('screens.about.authorMail'),
|
||||||
icon: 'git',
|
showChevron: true,
|
||||||
text: 'Git',
|
},
|
||||||
showChevron: true
|
{
|
||||||
},
|
onPressCallback: () => {
|
||||||
{
|
openWebLink(links.authorLinkedin);
|
||||||
onPressCallback: () => openWebLink(links.changelog),
|
},
|
||||||
icon: 'refresh',
|
icon: 'linkedin',
|
||||||
text: i18n.t('screens.about.changelog'),
|
text: 'Linkedin',
|
||||||
showChevron: true
|
showChevron: true,
|
||||||
},
|
},
|
||||||
{
|
];
|
||||||
onPressCallback: () => openWebLink(links.license),
|
|
||||||
icon: 'file-document',
|
|
||||||
text: i18n.t('screens.about.license'),
|
|
||||||
showChevron: true
|
|
||||||
},
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* Data to be displayed in the author card
|
|
||||||
*/
|
|
||||||
authorData = [
|
|
||||||
{
|
|
||||||
onPressCallback: () => openWebLink(links.meme),
|
|
||||||
icon: 'account-circle',
|
|
||||||
text: 'Arnaud VERGNET',
|
|
||||||
showChevron: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onPressCallback: () => openWebLink(links.authorMail),
|
|
||||||
icon: 'email',
|
|
||||||
text: i18n.t('screens.about.authorMail'),
|
|
||||||
showChevron: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onPressCallback: () => openWebLink(links.authorLinkedin),
|
|
||||||
icon: 'linkedin',
|
|
||||||
text: 'Linkedin',
|
|
||||||
showChevron: true
|
|
||||||
},
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* Data to be displayed in the additional developer card
|
|
||||||
*/
|
|
||||||
additionalDevData = [
|
|
||||||
{
|
|
||||||
onPressCallback: () => console.log('Meme this'),
|
|
||||||
icon: 'account',
|
|
||||||
text: 'Yohan SIMARD',
|
|
||||||
showChevron: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onPressCallback: () => openWebLink(links.yohanMail),
|
|
||||||
icon: 'email',
|
|
||||||
text: i18n.t('screens.about.authorMail'),
|
|
||||||
showChevron: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onPressCallback: () => openWebLink(links.yohanLinkedin),
|
|
||||||
icon: 'linkedin',
|
|
||||||
text: 'Linkedin',
|
|
||||||
showChevron: true
|
|
||||||
},
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* Data to be displayed in the technologies card
|
|
||||||
*/
|
|
||||||
technoData = [
|
|
||||||
{
|
|
||||||
onPressCallback: () => openWebLink(links.react),
|
|
||||||
icon: 'react',
|
|
||||||
text: i18n.t('screens.about.reactNative'),
|
|
||||||
showChevron: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onPressCallback: () => this.props.navigation.navigate('dependencies'),
|
|
||||||
icon: 'developer-board',
|
|
||||||
text: i18n.t('screens.about.libs'),
|
|
||||||
showChevron: true
|
|
||||||
},
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* Order of information cards
|
|
||||||
*/
|
|
||||||
dataOrder = [
|
|
||||||
{
|
|
||||||
id: 'app',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'team',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'techno',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the app icon
|
* Data to be displayed in the additional developer card
|
||||||
*
|
*/
|
||||||
* @param props
|
additionalDevData = [
|
||||||
* @return {*}
|
{
|
||||||
*/
|
onPressCallback: () => {
|
||||||
getAppIcon(props) {
|
console.log('Meme this');
|
||||||
return (
|
},
|
||||||
|
icon: 'account',
|
||||||
|
text: 'Yohan SIMARD',
|
||||||
|
showChevron: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
openWebLink(links.yohanMail);
|
||||||
|
},
|
||||||
|
icon: 'email',
|
||||||
|
text: i18n.t('screens.about.authorMail'),
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
openWebLink(links.yohanLinkedin);
|
||||||
|
},
|
||||||
|
icon: 'linkedin',
|
||||||
|
text: 'Linkedin',
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data to be displayed in the technologies card
|
||||||
|
*/
|
||||||
|
technoData = [
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
openWebLink(links.react);
|
||||||
|
},
|
||||||
|
icon: 'react',
|
||||||
|
text: i18n.t('screens.about.reactNative'),
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onPressCallback: () => {
|
||||||
|
const {navigation} = this.props;
|
||||||
|
navigation.navigate('dependencies');
|
||||||
|
},
|
||||||
|
icon: 'developer-board',
|
||||||
|
text: i18n.t('screens.about.libs'),
|
||||||
|
showChevron: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order of information cards
|
||||||
|
*/
|
||||||
|
dataOrder = [
|
||||||
|
{
|
||||||
|
id: 'app',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'team',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'techno',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the app card showing information and links about the app.
|
||||||
|
*
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
getAppCard(): React.Node {
|
||||||
|
return (
|
||||||
|
<Card style={{marginBottom: 10}}>
|
||||||
|
<Card.Title
|
||||||
|
title="Campus"
|
||||||
|
subtitle={packageJson.version}
|
||||||
|
left={({size}: {size: number}): React.Node => (
|
||||||
<Avatar.Image
|
<Avatar.Image
|
||||||
{...props}
|
size={size}
|
||||||
source={require('../../../assets/android.icon.png')}
|
source={APP_LOGO}
|
||||||
style={{backgroundColor: 'transparent'}}
|
style={{backgroundColor: 'transparent'}}
|
||||||
/>
|
/>
|
||||||
);
|
)}
|
||||||
|
/>
|
||||||
|
<Card.Content>
|
||||||
|
<FlatList
|
||||||
|
data={this.appData}
|
||||||
|
keyExtractor={this.keyExtractor}
|
||||||
|
renderItem={this.getCardItem}
|
||||||
|
/>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the team card showing information and links about the team
|
||||||
|
*
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
getTeamCard(): React.Node {
|
||||||
|
return (
|
||||||
|
<Card style={{marginBottom: 10}}>
|
||||||
|
<Card.Title
|
||||||
|
title={i18n.t('screens.about.team')}
|
||||||
|
left={({size, color}: {size: number, color: string}): React.Node => (
|
||||||
|
<Avatar.Icon size={size} color={color} icon="account-multiple" />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Card.Content>
|
||||||
|
<Title>{i18n.t('screens.about.author')}</Title>
|
||||||
|
<FlatList
|
||||||
|
data={this.authorData}
|
||||||
|
keyExtractor={this.keyExtractor}
|
||||||
|
listKey="1"
|
||||||
|
renderItem={this.getCardItem}
|
||||||
|
/>
|
||||||
|
<Title>{i18n.t('screens.about.additionalDev')}</Title>
|
||||||
|
<FlatList
|
||||||
|
data={this.additionalDevData}
|
||||||
|
keyExtractor={this.keyExtractor}
|
||||||
|
listKey="2"
|
||||||
|
renderItem={this.getCardItem}
|
||||||
|
/>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the techno card showing information and links about the technologies used in the app
|
||||||
|
*
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
getTechnoCard(): React.Node {
|
||||||
|
return (
|
||||||
|
<Card style={{marginBottom: 10}}>
|
||||||
|
<Card.Content>
|
||||||
|
<Title>{i18n.t('screens.about.technologies')}</Title>
|
||||||
|
<FlatList
|
||||||
|
data={this.technoData}
|
||||||
|
keyExtractor={this.keyExtractor}
|
||||||
|
renderItem={this.getCardItem}
|
||||||
|
/>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a chevron icon
|
||||||
|
*
|
||||||
|
* @param props
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static getChevronIcon({
|
||||||
|
size,
|
||||||
|
color,
|
||||||
|
}: {
|
||||||
|
size: number,
|
||||||
|
color: string,
|
||||||
|
}): React.Node {
|
||||||
|
return <List.Icon size={size} color={color} icon="chevron-right" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a custom list item icon
|
||||||
|
*
|
||||||
|
* @param item The item to show the icon for
|
||||||
|
* @param props
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
static getItemIcon(
|
||||||
|
item: ListItemType,
|
||||||
|
{size, color}: {size: number, color: string},
|
||||||
|
): React.Node {
|
||||||
|
return <List.Icon size={size} color={color} icon={item.icon} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a clickable card item to be rendered inside a card.
|
||||||
|
*
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
getCardItem = ({item}: {item: ListItemType}): React.Node => {
|
||||||
|
const getItemIcon = (props: {size: number, color: string}): React.Node =>
|
||||||
|
AboutScreen.getItemIcon(item, props);
|
||||||
|
if (item.showChevron) {
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
title={item.text}
|
||||||
|
left={getItemIcon}
|
||||||
|
right={AboutScreen.getChevronIcon}
|
||||||
|
onPress={item.onPressCallback}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
title={item.text}
|
||||||
|
left={getItemIcon}
|
||||||
|
onPress={item.onPressCallback}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts a key from the given item
|
* Gets a card, depending on the given item's id
|
||||||
*
|
*
|
||||||
* @param item The item to extract the key from
|
* @param item The item to show
|
||||||
* @return {string} The extracted key
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
keyExtractor(item: ListItem): string {
|
getMainCard = ({item}: {item: {id: string}}): React.Node => {
|
||||||
return item.icon;
|
switch (item.id) {
|
||||||
|
case 'app':
|
||||||
|
return this.getAppCard();
|
||||||
|
case 'team':
|
||||||
|
return this.getTeamCard();
|
||||||
|
case 'techno':
|
||||||
|
return this.getTechnoCard();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the app card showing information and links about the app.
|
* Extracts a key from the given item
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @param item The item to extract the key from
|
||||||
*/
|
* @return {string} The extracted key
|
||||||
getAppCard() {
|
*/
|
||||||
return (
|
keyExtractor = (item: ListItemType): string => item.icon;
|
||||||
<Card style={{marginBottom: 10}}>
|
|
||||||
<Card.Title
|
|
||||||
title={"Campus"}
|
|
||||||
subtitle={packageJson.version}
|
|
||||||
left={this.getAppIcon}/>
|
|
||||||
<Card.Content>
|
|
||||||
<FlatList
|
|
||||||
data={this.appData}
|
|
||||||
keyExtractor={this.keyExtractor}
|
|
||||||
renderItem={this.getCardItem}
|
|
||||||
/>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
render(): React.Node {
|
||||||
* Gets the team card showing information and links about the team
|
return (
|
||||||
*
|
<CollapsibleFlatList
|
||||||
* @return {*}
|
style={{padding: 5}}
|
||||||
*/
|
data={this.dataOrder}
|
||||||
getTeamCard() {
|
renderItem={this.getMainCard}
|
||||||
return (
|
/>
|
||||||
<Card style={{marginBottom: 10}}>
|
);
|
||||||
<Card.Title
|
}
|
||||||
title={i18n.t('screens.about.team')}
|
|
||||||
left={(props) => <Avatar.Icon {...props} icon={'account-multiple'}/>}/>
|
|
||||||
<Card.Content>
|
|
||||||
<Title>{i18n.t('screens.about.author')}</Title>
|
|
||||||
<FlatList
|
|
||||||
data={this.authorData}
|
|
||||||
keyExtractor={this.keyExtractor}
|
|
||||||
listKey={"1"}
|
|
||||||
renderItem={this.getCardItem}
|
|
||||||
/>
|
|
||||||
<Title>{i18n.t('screens.about.additionalDev')}</Title>
|
|
||||||
<FlatList
|
|
||||||
data={this.additionalDevData}
|
|
||||||
keyExtractor={this.keyExtractor}
|
|
||||||
listKey={"2"}
|
|
||||||
renderItem={this.getCardItem}
|
|
||||||
/>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the techno card showing information and links about the technologies used in the app
|
|
||||||
*
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
getTechnoCard() {
|
|
||||||
return (
|
|
||||||
<Card style={{marginBottom: 10}}>
|
|
||||||
<Card.Content>
|
|
||||||
<Title>{i18n.t('screens.about.technologies')}</Title>
|
|
||||||
<FlatList
|
|
||||||
data={this.technoData}
|
|
||||||
keyExtractor={this.keyExtractor}
|
|
||||||
renderItem={this.getCardItem}
|
|
||||||
/>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a chevron icon
|
|
||||||
*
|
|
||||||
* @param props
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
getChevronIcon(props) {
|
|
||||||
return (
|
|
||||||
<List.Icon {...props} icon={'chevron-right'}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a custom list item icon
|
|
||||||
*
|
|
||||||
* @param item The item to show the icon for
|
|
||||||
* @param props
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
getItemIcon(item: ListItem, props) {
|
|
||||||
return (
|
|
||||||
<List.Icon {...props} icon={item.icon}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a clickable card item to be rendered inside a card.
|
|
||||||
*
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
getCardItem = ({item}: { item: ListItem }) => {
|
|
||||||
const getItemIcon = this.getItemIcon.bind(this, item);
|
|
||||||
if (item.showChevron) {
|
|
||||||
return (
|
|
||||||
<List.Item
|
|
||||||
title={item.text}
|
|
||||||
left={getItemIcon}
|
|
||||||
right={this.getChevronIcon}
|
|
||||||
onPress={item.onPressCallback}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<List.Item
|
|
||||||
title={item.text}
|
|
||||||
left={getItemIcon}
|
|
||||||
onPress={item.onPressCallback}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a card, depending on the given item's id
|
|
||||||
*
|
|
||||||
* @param item The item to show
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
getMainCard = ({item}: { item: { id: string } }) => {
|
|
||||||
switch (item.id) {
|
|
||||||
case 'app':
|
|
||||||
return this.getAppCard();
|
|
||||||
case 'team':
|
|
||||||
return this.getTeamCard();
|
|
||||||
case 'techno':
|
|
||||||
return this.getTechnoCard();
|
|
||||||
}
|
|
||||||
return <View/>;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<CollapsibleFlatList
|
|
||||||
style={{padding: 5}}
|
|
||||||
data={this.dataOrder}
|
|
||||||
renderItem={this.getMainCard}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(AboutScreen);
|
export default withTheme(AboutScreen);
|
||||||
|
|
|
@ -1,183 +1,211 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View} from "react-native";
|
import {View} from 'react-native';
|
||||||
import AsyncStorageManager from "../../managers/AsyncStorageManager";
|
import {
|
||||||
import CustomModal from "../../components/Overrides/CustomModal";
|
Button,
|
||||||
import {Button, List, Subheading, TextInput, Title, withTheme} from 'react-native-paper';
|
List,
|
||||||
import {StackNavigationProp} from "@react-navigation/stack";
|
Subheading,
|
||||||
import {Modalize} from "react-native-modalize";
|
TextInput,
|
||||||
import type {CustomTheme} from "../../managers/ThemeManager";
|
Title,
|
||||||
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
|
withTheme,
|
||||||
|
} from 'react-native-paper';
|
||||||
|
import {Modalize} from 'react-native-modalize';
|
||||||
|
import CustomModal from '../../components/Overrides/CustomModal';
|
||||||
|
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||||
|
import type {CustomThemeType} from '../../managers/ThemeManager';
|
||||||
|
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
|
||||||
|
|
||||||
type PreferenceItem = {
|
type PreferenceItemType = {
|
||||||
key: string,
|
key: string,
|
||||||
default: string,
|
default: string,
|
||||||
current: string,
|
current: string,
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
navigation: StackNavigationProp,
|
|
||||||
theme: CustomTheme
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type PropsType = {
|
||||||
modalCurrentDisplayItem: PreferenceItem,
|
theme: CustomThemeType,
|
||||||
currentPreferences: Array<PreferenceItem>,
|
};
|
||||||
}
|
|
||||||
|
type StateType = {
|
||||||
|
modalCurrentDisplayItem: PreferenceItemType,
|
||||||
|
currentPreferences: Array<PreferenceItemType>,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining the Debug screen.
|
* Class defining the Debug screen.
|
||||||
* This screen allows the user to get and modify information on the app/device.
|
* This screen allows the user to get and modify information on the app/device.
|
||||||
*/
|
*/
|
||||||
class DebugScreen extends React.Component<Props, State> {
|
class DebugScreen extends React.Component<PropsType, StateType> {
|
||||||
|
modalRef: Modalize;
|
||||||
|
|
||||||
modalRef: Modalize;
|
modalInputValue: string;
|
||||||
modalInputValue: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies user preferences to state for easier manipulation
|
* Copies user preferences to state for easier manipulation
|
||||||
*
|
*
|
||||||
* @param props
|
* @param props
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor(props: PropsType) {
|
||||||
super(props);
|
super(props);
|
||||||
this.modalInputValue = "";
|
this.modalInputValue = '';
|
||||||
let currentPreferences : Array<PreferenceItem> = [];
|
const currentPreferences: Array<PreferenceItemType> = [];
|
||||||
Object.values(AsyncStorageManager.PREFERENCES).map((object: any) => {
|
// eslint-disable-next-line flowtype/no-weak-types
|
||||||
let newObject: PreferenceItem = {...object};
|
Object.values(AsyncStorageManager.PREFERENCES).forEach((object: any) => {
|
||||||
newObject.current = AsyncStorageManager.getString(newObject.key);
|
const newObject: PreferenceItemType = {...object};
|
||||||
currentPreferences.push(newObject);
|
newObject.current = AsyncStorageManager.getString(newObject.key);
|
||||||
});
|
currentPreferences.push(newObject);
|
||||||
this.state = {
|
});
|
||||||
modalCurrentDisplayItem: {},
|
this.state = {
|
||||||
currentPreferences: currentPreferences
|
modalCurrentDisplayItem: {},
|
||||||
};
|
currentPreferences,
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the edit modal
|
|
||||||
*
|
|
||||||
* @param item
|
|
||||||
*/
|
|
||||||
showEditModal(item: PreferenceItem) {
|
|
||||||
this.setState({
|
|
||||||
modalCurrentDisplayItem: item
|
|
||||||
});
|
|
||||||
if (this.modalRef) {
|
|
||||||
this.modalRef.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the edit modal content
|
|
||||||
*
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
getModalContent() {
|
|
||||||
return (
|
|
||||||
<View style={{
|
|
||||||
flex: 1,
|
|
||||||
padding: 20
|
|
||||||
}}>
|
|
||||||
<Title>{this.state.modalCurrentDisplayItem.key}</Title>
|
|
||||||
<Subheading>Default: {this.state.modalCurrentDisplayItem.default}</Subheading>
|
|
||||||
<Subheading>Current: {this.state.modalCurrentDisplayItem.current}</Subheading>
|
|
||||||
<TextInput
|
|
||||||
label='New Value'
|
|
||||||
onChangeText={(text) => this.modalInputValue = text}
|
|
||||||
/>
|
|
||||||
<View style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginTop: 10,
|
|
||||||
}}>
|
|
||||||
<Button
|
|
||||||
mode="contained"
|
|
||||||
dark={true}
|
|
||||||
color={this.props.theme.colors.success}
|
|
||||||
onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.modalInputValue)}>
|
|
||||||
Save new value
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
mode="contained"
|
|
||||||
dark={true}
|
|
||||||
color={this.props.theme.colors.danger}
|
|
||||||
onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.state.modalCurrentDisplayItem.default)}>
|
|
||||||
Reset to default
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
let index = -1;
|
|
||||||
for (let i = 0; i < this.state.currentPreferences.length; i++) {
|
|
||||||
if (this.state.currentPreferences[i].key === key) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the new value of the given preference
|
|
||||||
*
|
|
||||||
* @param key The pref key
|
|
||||||
* @param value The pref value
|
|
||||||
*/
|
|
||||||
saveNewPrefs(key: string, value: string) {
|
|
||||||
this.setState((prevState) => {
|
|
||||||
let currentPreferences = [...prevState.currentPreferences];
|
|
||||||
currentPreferences[this.findIndexOfKey(key)].current = value;
|
|
||||||
return {currentPreferences};
|
|
||||||
});
|
|
||||||
AsyncStorageManager.set(key, value);
|
|
||||||
this.modalRef.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback used when receiving the modal ref
|
|
||||||
*
|
|
||||||
* @param ref
|
|
||||||
*/
|
|
||||||
onModalRef = (ref: Modalize) => {
|
|
||||||
this.modalRef = ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderItem = ({item}: {item: PreferenceItem}) => {
|
|
||||||
return (
|
|
||||||
<List.Item
|
|
||||||
title={item.key}
|
|
||||||
description={'Click to edit'}
|
|
||||||
onPress={() => this.showEditModal(item)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
/**
|
||||||
return (
|
* Gets the edit modal content
|
||||||
<View>
|
*
|
||||||
<CustomModal onRef={this.onModalRef}>
|
* @return {*}
|
||||||
{this.getModalContent()}
|
*/
|
||||||
</CustomModal>
|
getModalContent(): React.Node {
|
||||||
{/*$FlowFixMe*/}
|
const {props, state} = this;
|
||||||
<CollapsibleFlatList
|
return (
|
||||||
data={this.state.currentPreferences}
|
<View
|
||||||
extraData={this.state.currentPreferences}
|
style={{
|
||||||
renderItem={this.renderItem}
|
flex: 1,
|
||||||
/>
|
padding: 20,
|
||||||
</View>
|
}}>
|
||||||
);
|
<Title>{state.modalCurrentDisplayItem.key}</Title>
|
||||||
|
<Subheading>
|
||||||
|
Default: {state.modalCurrentDisplayItem.default}
|
||||||
|
</Subheading>
|
||||||
|
<Subheading>
|
||||||
|
Current: {state.modalCurrentDisplayItem.current}
|
||||||
|
</Subheading>
|
||||||
|
<TextInput
|
||||||
|
label="New Value"
|
||||||
|
onChangeText={(text: string) => {
|
||||||
|
this.modalInputValue = text;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginTop: 10,
|
||||||
|
}}>
|
||||||
|
<Button
|
||||||
|
mode="contained"
|
||||||
|
dark
|
||||||
|
color={props.theme.colors.success}
|
||||||
|
onPress={() => {
|
||||||
|
this.saveNewPrefs(
|
||||||
|
state.modalCurrentDisplayItem.key,
|
||||||
|
this.modalInputValue,
|
||||||
|
);
|
||||||
|
}}>
|
||||||
|
Save new value
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
mode="contained"
|
||||||
|
dark
|
||||||
|
color={props.theme.colors.danger}
|
||||||
|
onPress={() => {
|
||||||
|
this.saveNewPrefs(
|
||||||
|
state.modalCurrentDisplayItem.key,
|
||||||
|
state.modalCurrentDisplayItem.default,
|
||||||
|
);
|
||||||
|
}}>
|
||||||
|
Reset to default
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRenderItem = ({item}: {item: PreferenceItemType}): React.Node => {
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
title={item.key}
|
||||||
|
description="Click to edit"
|
||||||
|
onPress={() => {
|
||||||
|
this.showEditModal(item);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback used when receiving the modal ref
|
||||||
|
*
|
||||||
|
* @param ref
|
||||||
|
*/
|
||||||
|
onModalRef = (ref: Modalize) => {
|
||||||
|
this.modalRef = ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the edit modal
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
showEditModal(item: PreferenceItemType) {
|
||||||
|
this.setState({
|
||||||
|
modalCurrentDisplayItem: item,
|
||||||
|
});
|
||||||
|
if (this.modalRef) this.modalRef.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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): number {
|
||||||
|
const {currentPreferences} = this.state;
|
||||||
|
let index = -1;
|
||||||
|
for (let i = 0; i < currentPreferences.length; i += 1) {
|
||||||
|
if (currentPreferences[i].key === key) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the new value of the given preference
|
||||||
|
*
|
||||||
|
* @param key The pref key
|
||||||
|
* @param value The pref value
|
||||||
|
*/
|
||||||
|
saveNewPrefs(key: string, value: string) {
|
||||||
|
this.setState((prevState: StateType): {
|
||||||
|
currentPreferences: Array<PreferenceItemType>,
|
||||||
|
} => {
|
||||||
|
const currentPreferences = [...prevState.currentPreferences];
|
||||||
|
currentPreferences[this.findIndexOfKey(key)].current = value;
|
||||||
|
return {currentPreferences};
|
||||||
|
});
|
||||||
|
AsyncStorageManager.set(key, value);
|
||||||
|
this.modalRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): React.Node {
|
||||||
|
const {state} = this;
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<CustomModal onRef={this.onModalRef}>
|
||||||
|
{this.getModalContent()}
|
||||||
|
</CustomModal>
|
||||||
|
{/* $FlowFixMe */}
|
||||||
|
<CollapsibleFlatList
|
||||||
|
data={state.currentPreferences}
|
||||||
|
extraData={state.currentPreferences}
|
||||||
|
renderItem={this.getRenderItem}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(DebugScreen);
|
export default withTheme(DebugScreen);
|
||||||
|
|
Loading…
Reference in a new issue