Update clubs screens to use TypeScript

This commit is contained in:
Arnaud Vergnet 2020-09-22 19:08:44 +02:00
parent f7e767748a
commit 5977ce257b
5 changed files with 158 additions and 164 deletions

View file

@ -27,7 +27,7 @@ import type {
type PropsType = {
onPress: () => void;
categoryTranslator: (id: number) => ClubCategoryType;
categoryTranslator: (id: number) => ClubCategoryType | null;
item: ClubType;
height: number;
theme: ReactNativePaper.Theme;
@ -50,14 +50,16 @@ class ClubListItem extends React.Component<PropsType> {
const final: Array<React.ReactNode> = [];
categories.forEach((cat: number | null) => {
if (cat != null) {
const category: ClubCategoryType = props.categoryTranslator(cat);
final.push(
<Chip
style={{marginRight: 5, marginBottom: 5}}
key={`${props.item.id}:${category.id}`}>
{category.name}
</Chip>,
);
const category = props.categoryTranslator(cat);
if (category) {
final.push(
<Chip
style={{marginRight: 5, marginBottom: 5}}
key={`${props.item.id}:${category.id}`}>
{category.name}
</Chip>,
);
}
}
});
return <View style={{flexDirection: 'row'}}>{final}</View>;

View file

@ -1,72 +0,0 @@
/*
* Copyright (c) 2019 - 2020 Arnaud Vergnet.
*
* This file is part of Campus INSAT.
*
* Campus INSAT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Campus INSAT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
// @flow
import * as React from 'react';
import {Image, View} from 'react-native';
import {Card, Avatar, Text, withTheme} from 'react-native-paper';
import i18n from 'i18n-js';
import Autolink from 'react-native-autolink';
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
import AMICALE_ICON from '../../../../assets/amicale.png';
import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
const CONTACT_LINK = 'clubs@amicale-insat.fr';
// eslint-disable-next-line react/prefer-stateless-function
class ClubAboutScreen extends React.Component<null> {
render(): React.Node {
return (
<CollapsibleScrollView style={{padding: 5}}>
<View
style={{
width: '100%',
height: 100,
marginTop: 20,
marginBottom: 20,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={AMICALE_ICON}
style={{flex: 1, resizeMode: 'contain'}}
resizeMode="contain"
/>
</View>
<Text>{i18n.t('screens.clubs.about.text')}</Text>
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t('screens.clubs.about.title')}
subtitle={i18n.t('screens.clubs.about.subtitle')}
left={(iconProps: CardTitleIconPropsType): React.Node => (
<Avatar.Icon size={iconProps.size} icon="information" />
)}
/>
<Card.Content>
<Text>{i18n.t('screens.clubs.about.message')}</Text>
<Autolink text={CONTACT_LINK} component={Text} />
</Card.Content>
</Card>
</CollapsibleScrollView>
);
}
}
export default withTheme(ClubAboutScreen);

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2019 - 2020 Arnaud Vergnet.
*
* This file is part of Campus INSAT.
*
* Campus INSAT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Campus INSAT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
import * as React from 'react';
import {Image, View} from 'react-native';
import {Card, Avatar, Text} from 'react-native-paper';
import i18n from 'i18n-js';
import Autolink from 'react-native-autolink';
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
const AMICALE_ICON = require('../../../../assets/amicale.png');
const CONTACT_LINK = 'clubs@amicale-insat.fr';
function ClubAboutScreen() {
return (
<CollapsibleScrollView style={{padding: 5}}>
<View
style={{
width: '100%',
height: 100,
marginTop: 20,
marginBottom: 20,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={AMICALE_ICON}
style={{flex: 1, resizeMode: 'contain'}}
resizeMode="contain"
/>
</View>
<Text>{i18n.t('screens.clubs.about.text')}</Text>
<Card style={{margin: 5}}>
<Card.Title
title={i18n.t('screens.clubs.about.title')}
subtitle={i18n.t('screens.clubs.about.subtitle')}
left={(iconProps) => (
<Avatar.Icon size={iconProps.size} icon="information" />
)}
/>
<Card.Content>
<Text>{i18n.t('screens.clubs.about.message')}</Text>
<Autolink<typeof Text> text={CONTACT_LINK} component={Text} />
</Card.Content>
</Card>
</CollapsibleScrollView>
);
}
export default ClubAboutScreen;

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
// @flow
import * as React from 'react';
import {Linking, View} from 'react-native';
import {
@ -35,24 +33,20 @@ import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen
import CustomHTML from '../../../components/Overrides/CustomHTML';
import CustomTabBar from '../../../components/Tabbar/CustomTabBar';
import type {ClubCategoryType, ClubType} from './ClubListScreen';
import type {CustomThemeType} from '../../../managers/ThemeManager';
import {ERROR_TYPE} from '../../../utils/WebData';
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
import type {ApiGenericDataType} from '../../../utils/WebData';
import type {CardTitleIconPropsType} from '../../../constants/PaperStyles';
import ImageGalleryButton from '../../../components/Media/ImageGalleryButton';
type PropsType = {
navigation: StackNavigationProp,
navigation: StackNavigationProp<any>;
route: {
params?: {
data?: ClubType,
categories?: Array<ClubCategoryType>,
clubId?: number,
},
...
},
theme: CustomThemeType,
data?: ClubType;
categories?: Array<ClubCategoryType>;
clubId?: number;
};
};
theme: ReactNativePaper.Theme;
};
const AMICALE_MAIL = 'clubs@amicale-insat.fr';
@ -73,21 +67,20 @@ class ClubDisplayScreen extends React.Component<PropsType> {
constructor(props: PropsType) {
super(props);
if (props.route.params != null) {
if (
props.route.params.data != null &&
props.route.params.categories != null
) {
this.displayData = props.route.params.data;
this.categories = props.route.params.categories;
this.clubId = props.route.params.data.id;
this.shouldFetchData = false;
} else if (props.route.params.clubId != null) {
this.displayData = null;
this.categories = null;
this.clubId = props.route.params.clubId;
this.shouldFetchData = true;
}
this.displayData = null;
this.categories = null;
this.clubId = props.route.params?.clubId ? props.route.params.clubId : 0;
this.shouldFetchData = true;
if (
props.route.params &&
props.route.params.data &&
props.route.params.categories
) {
this.displayData = props.route.params.data;
this.categories = props.route.params.categories;
this.clubId = props.route.params.data.id;
this.shouldFetchData = false;
}
}
@ -101,7 +94,9 @@ class ClubDisplayScreen extends React.Component<PropsType> {
let categoryName = '';
if (this.categories !== null) {
this.categories.forEach((item: ClubCategoryType) => {
if (id === item.id) categoryName = item.name;
if (id === item.id) {
categoryName = item.name;
}
});
}
return categoryName;
@ -113,10 +108,12 @@ class ClubDisplayScreen extends React.Component<PropsType> {
* @param categories The categories to display (max 2)
* @returns {null|*}
*/
getCategoriesRender(categories: Array<number | null>): React.Node {
if (this.categories == null) return null;
getCategoriesRender(categories: Array<number | null>) {
if (this.categories == null) {
return null;
}
const final = [];
const final: Array<React.ReactNode> = [];
categories.forEach((cat: number | null) => {
if (cat != null) {
final.push(
@ -136,9 +133,9 @@ class ClubDisplayScreen extends React.Component<PropsType> {
* @param email The club contact email
* @returns {*}
*/
getManagersRender(managers: Array<string>, email: string | null): React.Node {
getManagersRender(managers: Array<string>, email: string | null) {
const {props} = this;
const managersListView = [];
const managersListView: Array<React.ReactNode> = [];
managers.forEach((item: string) => {
managersListView.push(<Paragraph key={item}>{item}</Paragraph>);
});
@ -153,7 +150,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
? i18n.t('screens.clubs.managersSubtitle')
: i18n.t('screens.clubs.managersUnavailable')
}
left={(iconProps: CardTitleIconPropsType): React.Node => (
left={(iconProps) => (
<Avatar.Icon
size={iconProps.size}
style={{backgroundColor: 'transparent'}}
@ -181,10 +178,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
* @param hasManagers True if the club has managers
* @returns {*}
*/
static getEmailButton(
email: string | null,
hasManagers: boolean,
): React.Node {
static getEmailButton(email: string | null, hasManagers: boolean) {
const destinationEmail =
email != null && hasManagers ? email : AMICALE_MAIL;
const text =
@ -206,20 +200,15 @@ class ClubDisplayScreen extends React.Component<PropsType> {
);
}
getScreen = (response: Array<ApiGenericDataType | null>): React.Node => {
const {navigation} = this.props;
let data: ClubType | null = null;
if (response[0] != null) {
[data] = response;
this.updateHeaderTitle(data);
}
getScreen = (response: Array<ClubType | null>) => {
let data: ClubType | null = response[0];
if (data != null) {
this.updateHeaderTitle(data);
return (
<CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
{this.getCategoriesRender(data.category)}
{data.logo !== null ? (
<ImageGalleryButton
navigation={navigation}
images={[{url: data.logo}]}
style={{
width: 300,
@ -259,9 +248,9 @@ class ClubDisplayScreen extends React.Component<PropsType> {
props.navigation.setOptions({title: data.name});
}
render(): React.Node {
render() {
const {props} = this;
if (this.shouldFetchData)
if (this.shouldFetchData) {
return (
<AuthenticatedScreen
navigation={props.navigation}
@ -283,6 +272,7 @@ class ClubDisplayScreen extends React.Component<PropsType> {
]}
/>
);
}
return this.getScreen([this.displayData]);
}
}

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
// @flow
import * as React from 'react';
import {Platform} from 'react-native';
import {Searchbar} from 'react-native-paper';
@ -34,27 +32,27 @@ import MaterialHeaderButtons, {
import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
export type ClubCategoryType = {
id: number,
name: string,
id: number;
name: string;
};
export type ClubType = {
id: number,
name: string,
description: string,
logo: string,
email: string | null,
category: Array<number | null>,
responsibles: Array<string>,
id: number;
name: string;
description: string;
logo: string;
email: string | null;
category: Array<number | null>;
responsibles: Array<string>;
};
type PropsType = {
navigation: StackNavigationProp,
navigation: StackNavigationProp<any>;
};
type StateType = {
currentlySelectedCategories: Array<number>,
currentSearchString: string,
currentlySelectedCategories: Array<number>;
currentSearchString: string;
};
const LIST_ITEM_HEIGHT = 96;
@ -62,8 +60,9 @@ const LIST_ITEM_HEIGHT = 96;
class ClubListScreen extends React.Component<PropsType, StateType> {
categories: Array<ClubCategoryType>;
constructor() {
super();
constructor(props: PropsType) {
super(props);
this.categories = [];
this.state = {
currentlySelectedCategories: [],
currentSearchString: '',
@ -114,7 +113,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
*
* @return {*}
*/
getSearchBar = (): React.Node => {
getSearchBar = () => {
return (
<Searchbar
placeholder={i18n.t('screens.proximo.search')}
@ -131,7 +130,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
* Gets the header button
* @return {*}
*/
getHeaderButtons = (): React.Node => {
getHeaderButtons = () => {
const onPress = () => {
const {props} = this;
props.navigation.navigate('club-about');
@ -145,12 +144,12 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
getScreen = (
data: Array<{
categories: Array<ClubCategoryType>,
clubs: Array<ClubType>,
categories: Array<ClubCategoryType>;
clubs: Array<ClubType>;
} | null>,
): React.Node => {
let categoryList = [];
let clubList = [];
) => {
let categoryList: Array<ClubCategoryType> = [];
let clubList: Array<ClubType> = [];
if (data[0] != null) {
categoryList = data[0].categories;
clubList = data[0].clubs;
@ -174,7 +173,7 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
*
* @returns {*}
*/
getListHeader(): React.Node {
getListHeader() {
const {state} = this;
return (
<ClubListHeader
@ -194,12 +193,14 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
getCategoryOfId = (id: number): ClubCategoryType | null => {
let cat = null;
this.categories.forEach((item: ClubCategoryType) => {
if (id === item.id) cat = item;
if (id === item.id) {
cat = item;
}
});
return cat;
};
getRenderItem = ({item}: {item: ClubType}): React.Node => {
getRenderItem = ({item}: {item: ClubType}) => {
const onPress = () => {
this.onListItemPress(item);
};
@ -219,9 +220,9 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
keyExtractor = (item: ClubType): string => item.id.toString();
itemLayout = (
data: {...},
data: Array<ClubType> | null | undefined,
index: number,
): {length: number, offset: number, index: number} => ({
): {length: number; offset: number; index: number} => ({
length: LIST_ITEM_HEIGHT,
offset: LIST_ITEM_HEIGHT * index,
index,
@ -240,17 +241,23 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
const {state} = this;
const newCategoriesState = [...state.currentlySelectedCategories];
let newStrState = state.currentSearchString;
if (filterStr !== null) newStrState = filterStr;
if (filterStr !== null) {
newStrState = filterStr;
}
if (categoryId !== null) {
const index = newCategoriesState.indexOf(categoryId);
if (index === -1) newCategoriesState.push(categoryId);
else newCategoriesState.splice(index, 1);
if (index === -1) {
newCategoriesState.push(categoryId);
} else {
newCategoriesState.splice(index, 1);
}
}
if (filterStr !== null || categoryId !== null)
if (filterStr !== null || categoryId !== null) {
this.setState({
currentSearchString: newStrState,
currentlySelectedCategories: newCategoriesState,
});
}
}
/**
@ -264,12 +271,13 @@ class ClubListScreen extends React.Component<PropsType, StateType> {
let shouldRender =
state.currentlySelectedCategories.length === 0 ||
isItemInCategoryFilter(state.currentlySelectedCategories, item.category);
if (shouldRender)
if (shouldRender) {
shouldRender = stringMatchQuery(item.name, state.currentSearchString);
}
return shouldRender;
}
render(): React.Node {
render() {
const {props} = this;
return (
<AuthenticatedScreen