Improve about components to match linter

This commit is contained in:
Arnaud Vergnet 2020-08-05 00:37:51 +02:00
parent 3e4f2f4ac1
commit 483970c9a8
3 changed files with 601 additions and 532 deletions

View file

@ -1,36 +1,31 @@
// @flow
import * as React from 'react';
import packageJson from '../../../package';
import {List} from 'react-native-paper';
import {StackNavigationProp} from "@react-navigation/stack";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
import {View} from "react-native-animatable";
import {View} from 'react-native-animatable';
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
import packageJson from '../../../package.json';
type listItem = {
name: string,
version: string
type ListItemType = {
name: string,
version: string,
};
/**
* Generates the dependencies list from the raw json
*
* @param object The raw json
* @return {Array<listItem>}
* @return {Array<ListItemType>}
*/
function generateListFromObject(object: { [key: string]: string }): Array<listItem> {
let list = [];
let keys = Object.keys(object);
let values = Object.values(object);
for (let i = 0; i < keys.length; i++) {
list.push({name: keys[i], version: values[i]});
}
//$FlowFixMe
return list;
}
type Props = {
navigation: StackNavigationProp,
function generateListFromObject(object: {
[key: string]: string,
}): Array<ListItemType> {
const list = [];
const keys = Object.keys(object);
keys.forEach((key: string) => {
list.push({name: key, version: object[key]});
});
return list;
}
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
*/
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() {
super();
this.data = generateListFromObject(packageJson.dependencies);
}
keyExtractor = (item: ListItemType): string => item.name;
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 }) =>
<List.Item
title={item.name}
description={item.version.replace('^', '').replace('~', '')}
style={{height: LIST_ITEM_HEIGHT}}
/>;
getItemLayout = (
data: ListItemType,
index: number,
): {length: number, offset: number, index: number} => ({
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() {
return (
<View>
<CollapsibleFlatList
data={this.data}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
removeClippedSubviews={true}
getItemLayout={this.itemLayout}
/>
</View>
);
}
render(): React.Node {
return (
<View>
<CollapsibleFlatList
data={this.data}
keyExtractor={this.keyExtractor}
renderItem={this.getRenderItem}
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
removeClippedSubviews
getItemLayout={this.getItemLayout}
/>
</View>
);
}
}

View file

@ -1,351 +1,390 @@
// @flow
import * as React from 'react';
import {FlatList, Linking, Platform, View} from 'react-native';
import i18n from "i18n-js";
import {FlatList, Linking, Platform} from 'react-native';
import i18n from 'i18n-js';
import {Avatar, Card, List, Title, withTheme} from 'react-native-paper';
import packageJson from "../../../package.json";
import {StackNavigationProp} from "@react-navigation/stack";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
import {StackNavigationProp} from '@react-navigation/stack';
import packageJson from '../../../package.json';
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
import APP_LOGO from '../../../assets/android.icon.png';
type ListItem = {
onPressCallback: () => void,
icon: string,
text: string,
showChevron: boolean
type ListItemType = {
onPressCallback: () => void,
icon: string,
text: string,
showChevron: boolean,
};
const links = {
appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148',
playstore: 'https://play.google.com/store/apps/details?id=fr.amicaleinsat.application',
git: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/README.md',
changelog: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md',
license: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE',
authorMail: "mailto:vergnet@etud.insa-toulouse.fr?" +
"subject=" +
"Application Amicale INSA Toulouse" +
"&body=" +
"Coucou !\n\n",
authorLinkedin: 'https://www.linkedin.com/in/arnaud-vergnet-434ba5179/',
yohanMail: "mailto:ysimard@etud.insa-toulouse.fr?" +
"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"
appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148',
playstore:
'https://play.google.com/store/apps/details?id=fr.amicaleinsat.application',
git:
'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/README.md',
changelog:
'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md',
license:
'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE',
authorMail:
'mailto:vergnet@etud.insa-toulouse.fr?' +
'subject=' +
'Application Amicale INSA Toulouse' +
'&body=' +
'Coucou !\n\n',
authorLinkedin: 'https://www.linkedin.com/in/arnaud-vergnet-434ba5179/',
yohanMail:
'mailto:ysimard@etud.insa-toulouse.fr?' +
'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 = {
navigation: StackNavigationProp,
type PropsType = {
navigation: StackNavigationProp,
};
/**
* Opens a link in the device's browser
* @param link The link to open
*/
function openWebLink(link) {
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
function openWebLink(link: string) {
Linking.openURL(link);
}
/**
* 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
*/
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: () => 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 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',
},
];
/**
* 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,
},
];
/**
* Gets the app icon
*
* @param props
* @return {*}
*/
getAppIcon(props) {
return (
/**
* 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: () => {
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
{...props}
source={require('../../../assets/android.icon.png')}
style={{backgroundColor: 'transparent'}}
size={size}
source={APP_LOGO}
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
*
* @param item The item to extract the key from
* @return {string} The extracted key
*/
keyExtractor(item: ListItem): string {
return item.icon;
/**
* Gets a card, depending on the given item's id
*
* @param item The item to show
* @return {*}
*/
getMainCard = ({item}: {item: {id: string}}): React.Node => {
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.
*
* @return {*}
*/
getAppCard() {
return (
<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>
);
}
/**
* Extracts a key from the given item
*
* @param item The item to extract the key from
* @return {string} The extracted key
*/
keyExtractor = (item: ListItemType): string => item.icon;
/**
* Gets the team card showing information and links about the team
*
* @return {*}
*/
getTeamCard() {
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}
/>
);
}
render(): React.Node {
return (
<CollapsibleFlatList
style={{padding: 5}}
data={this.dataOrder}
renderItem={this.getMainCard}
/>
);
}
}
export default withTheme(AboutScreen);

View file

@ -1,183 +1,211 @@
// @flow
import * as React from 'react';
import {View} from "react-native";
import AsyncStorageManager from "../../managers/AsyncStorageManager";
import CustomModal from "../../components/Overrides/CustomModal";
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";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
import {View} from 'react-native';
import {
Button,
List,
Subheading,
TextInput,
Title,
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 = {
key: string,
default: string,
current: string,
}
type Props = {
navigation: StackNavigationProp,
theme: CustomTheme
type PreferenceItemType = {
key: string,
default: string,
current: string,
};
type State = {
modalCurrentDisplayItem: PreferenceItem,
currentPreferences: Array<PreferenceItem>,
}
type PropsType = {
theme: CustomThemeType,
};
type StateType = {
modalCurrentDisplayItem: PreferenceItemType,
currentPreferences: Array<PreferenceItemType>,
};
/**
* Class defining the Debug screen.
* 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
*
* @param props
*/
constructor(props) {
super(props);
this.modalInputValue = "";
let currentPreferences : Array<PreferenceItem> = [];
Object.values(AsyncStorageManager.PREFERENCES).map((object: any) => {
let newObject: PreferenceItem = {...object};
newObject.current = AsyncStorageManager.getString(newObject.key);
currentPreferences.push(newObject);
});
this.state = {
modalCurrentDisplayItem: {},
currentPreferences: 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)}
/>
);
/**
* Copies user preferences to state for easier manipulation
*
* @param props
*/
constructor(props: PropsType) {
super(props);
this.modalInputValue = '';
const currentPreferences: Array<PreferenceItemType> = [];
// eslint-disable-next-line flowtype/no-weak-types
Object.values(AsyncStorageManager.PREFERENCES).forEach((object: any) => {
const newObject: PreferenceItemType = {...object};
newObject.current = AsyncStorageManager.getString(newObject.key);
currentPreferences.push(newObject);
});
this.state = {
modalCurrentDisplayItem: {},
currentPreferences,
};
}
render() {
return (
<View>
<CustomModal onRef={this.onModalRef}>
{this.getModalContent()}
</CustomModal>
{/*$FlowFixMe*/}
<CollapsibleFlatList
data={this.state.currentPreferences}
extraData={this.state.currentPreferences}
renderItem={this.renderItem}
/>
</View>
);
/**
* Gets the edit modal content
*
* @return {*}
*/
getModalContent(): React.Node {
const {props, state} = this;
return (
<View
style={{
flex: 1,
padding: 20,
}}>
<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);