Update services screens to use TypeScript

This commit is contained in:
Arnaud Vergnet 2020-09-22 22:40:38 +02:00
parent 2eb4a3e0c1
commit 38afbf02a3
4 changed files with 71 additions and 90 deletions

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {View} from 'react-native'; import {View} from 'react-native';
import {Card, Text, withTheme} from 'react-native-paper'; import {Card, Text, withTheme} from 'react-native-paper';
@ -26,32 +24,31 @@ import {StackNavigationProp} from '@react-navigation/stack';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import DateManager from '../../managers/DateManager'; import DateManager from '../../managers/DateManager';
import WebSectionList from '../../components/Screens/WebSectionList'; import WebSectionList from '../../components/Screens/WebSectionList';
import type {CustomThemeType} from '../../managers/ThemeManager';
import type {SectionListDataType} from '../../components/Screens/WebSectionList'; import type {SectionListDataType} from '../../components/Screens/WebSectionList';
const DATA_URL = const DATA_URL =
'https://etud.insa-toulouse.fr/~amicale_app/menu/menu_data.json'; 'https://etud.insa-toulouse.fr/~amicale_app/menu/menu_data.json';
type PropsType = { type PropsType = {
navigation: StackNavigationProp, navigation: StackNavigationProp<any>;
theme: CustomThemeType, theme: ReactNativePaper.Theme;
}; };
export type RuFoodCategoryType = { export type RuFoodCategoryType = {
name: string, name: string;
dishes: Array<{name: string}>, dishes: Array<{name: string}>;
}; };
type RuMealType = { type RuMealType = {
name: string, name: string;
foodcategory: Array<RuFoodCategoryType>, foodcategory: Array<RuFoodCategoryType>;
}; };
type RawRuMenuType = { type RawRuMenuType = {
restaurant_id: number, restaurant_id: number;
id: number, id: number;
date: string, date: string;
meal: Array<RuMealType>, meal: Array<RuMealType>;
}; };
/** /**
@ -77,7 +74,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
createDataset = ( createDataset = (
fetchedData: Array<RawRuMenuType>, fetchedData: Array<RawRuMenuType>,
): SectionListDataType<RuFoodCategoryType> => { ): SectionListDataType<RuFoodCategoryType> => {
let result = []; let result: SectionListDataType<RuFoodCategoryType> = [];
if (fetchedData == null || fetchedData.length === 0) { if (fetchedData == null || fetchedData.length === 0) {
result = [ result = [
{ {
@ -104,11 +101,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
* @param section The section to render the header from * @param section The section to render the header from
* @return {*} * @return {*}
*/ */
getRenderSectionHeader = ({ getRenderSectionHeader = ({section}: {section: {title: string}}) => {
section,
}: {
section: {title: string},
}): React.Node => {
return ( return (
<Card <Card
style={{ style={{
@ -141,7 +134,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
* @param item The item to render * @param item The item to render
* @return {*} * @return {*}
*/ */
getRenderItem = ({item}: {item: RuFoodCategoryType}): React.Node => { getRenderItem = ({item}: {item: RuFoodCategoryType}) => {
const {theme} = this.props; const {theme} = this.props;
return ( return (
<Card <Card
@ -163,7 +156,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
}} }}
/> />
<Card.Content> <Card.Content>
{item.dishes.map((object: {name: string}): React.Node => {item.dishes.map((object: {name: string}) =>
object.name !== '' ? ( object.name !== '' ? (
<Text <Text
style={{ style={{
@ -188,7 +181,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
*/ */
getKeyExtractor = (item: RuFoodCategoryType): string => item.name; getKeyExtractor = (item: RuFoodCategoryType): string => item.name;
render(): React.Node { render() {
const {navigation} = this.props; const {navigation} = this.props;
return ( return (
<WebSectionList <WebSectionList

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {Image, View} from 'react-native'; import {Image, View} from 'react-native';
import { import {
@ -32,7 +30,6 @@ import {
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import {StackNavigationProp} from '@react-navigation/stack'; import {StackNavigationProp} from '@react-navigation/stack';
import CardList from '../../components/Lists/CardList/CardList'; import CardList from '../../components/Lists/CardList/CardList';
import type {CustomThemeType} from '../../managers/ThemeManager';
import MaterialHeaderButtons, { import MaterialHeaderButtons, {
Item, Item,
} from '../../components/Overrides/CustomHeaderButton'; } from '../../components/Overrides/CustomHeaderButton';
@ -46,8 +43,8 @@ import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatLis
import type {ServiceCategoryType} from '../../managers/ServicesManager'; import type {ServiceCategoryType} from '../../managers/ServicesManager';
type PropsType = { type PropsType = {
navigation: StackNavigationProp, navigation: StackNavigationProp<any>;
theme: CustomThemeType, theme: ReactNativePaper.Theme;
}; };
class ServicesScreen extends React.Component<PropsType> { class ServicesScreen extends React.Component<PropsType> {
@ -68,7 +65,7 @@ class ServicesScreen extends React.Component<PropsType> {
}); });
} }
getAboutButton = (): React.Node => ( getAboutButton = () => (
<MaterialHeaderButtons> <MaterialHeaderButtons>
<Item <Item
title="information" title="information"
@ -92,12 +89,11 @@ class ServicesScreen extends React.Component<PropsType> {
* @param source The source image to display. Can be a string for icons or a number for local images * @param source The source image to display. Can be a string for icons or a number for local images
* @returns {*} * @returns {*}
*/ */
getListTitleImage(source: string | number): React.Node { getListTitleImage(source: string | number) {
const {props} = this; const {props} = this;
if (typeof source === 'number') if (typeof source === 'number') {
return ( return (
<Image <Image
size={48}
source={source} source={source}
style={{ style={{
width: 48, width: 48,
@ -105,6 +101,7 @@ class ServicesScreen extends React.Component<PropsType> {
}} }}
/> />
); );
}
return ( return (
<Avatar.Icon <Avatar.Icon
size={48} size={48}
@ -121,7 +118,7 @@ class ServicesScreen extends React.Component<PropsType> {
* @param item * @param item
* @returns {*} * @returns {*}
*/ */
getRenderItem = ({item}: {item: ServiceCategoryType}): React.Node => { getRenderItem = ({item}: {item: ServiceCategoryType}) => {
const {props} = this; const {props} = this;
return ( return (
<TouchableRipple <TouchableRipple
@ -136,8 +133,8 @@ class ServicesScreen extends React.Component<PropsType> {
<Card.Title <Card.Title
title={item.title} title={item.title}
subtitle={item.subtitle} subtitle={item.subtitle}
left={(): React.Node => this.getListTitleImage(item.image)} left={() => this.getListTitleImage(item.image)}
right={(): React.Node => <List.Icon icon="chevron-right" />} right={() => <List.Icon icon="chevron-right" />}
/> />
<CardList dataset={item.content} isHorizontal /> <CardList dataset={item.content} isHorizontal />
</View> </View>
@ -147,14 +144,14 @@ class ServicesScreen extends React.Component<PropsType> {
keyExtractor = (item: ServiceCategoryType): string => item.title; keyExtractor = (item: ServiceCategoryType): string => item.title;
render(): React.Node { render() {
return ( return (
<View> <View>
<CollapsibleFlatList <CollapsibleFlatList
data={this.finalDataset} data={this.finalDataset}
renderItem={this.getRenderItem} renderItem={this.getRenderItem}
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
ItemSeparatorComponent={(): React.Node => <Divider />} ItemSeparatorComponent={() => <Divider />}
hasTab hasTab
/> />
<MascotPopup <MascotPopup
@ -163,7 +160,6 @@ class ServicesScreen extends React.Component<PropsType> {
message={i18n.t('screens.services.mascotDialog.message')} message={i18n.t('screens.services.mascotDialog.message')}
icon="cloud-question" icon="cloud-question"
buttons={{ buttons={{
action: null,
cancel: { cancel: {
message: i18n.t('screens.services.mascotDialog.button'), message: i18n.t('screens.services.mascotDialog.button'),
icon: 'check', icon: 'check',

View file

@ -17,28 +17,25 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {Collapsible} from 'react-navigation-collapsible'; import {Collapsible} from 'react-navigation-collapsible';
import {CommonActions} from '@react-navigation/native'; import {CommonActions} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack'; import {StackNavigationProp} from '@react-navigation/stack';
import CardList from '../../components/Lists/CardList/CardList'; import CardList from '../../components/Lists/CardList/CardList';
import CustomTabBar from '../../components/Tabbar/CustomTabBar';
import withCollapsible from '../../utils/withCollapsible';
import type {ServiceCategoryType} from '../../managers/ServicesManager'; import type {ServiceCategoryType} from '../../managers/ServicesManager';
type PropsType = { type PropsType = {
navigation: StackNavigationProp, navigation: StackNavigationProp<any>;
route: {params: {data: ServiceCategoryType | null}}, route: {params: {data: ServiceCategoryType | null}};
collapsibleStack: Collapsible, collapsibleStack: Collapsible;
}; };
class ServicesSectionScreen extends React.Component<PropsType> { class ServicesSectionScreen extends React.Component<PropsType> {
finalDataset: ServiceCategoryType; finalDataset: null | ServiceCategoryType;
constructor(props: PropsType) { constructor(props: PropsType) {
super(props); super(props);
this.finalDataset = null;
this.handleNavigationParams(); this.handleNavigationParams();
} }
@ -47,38 +44,24 @@ class ServicesSectionScreen extends React.Component<PropsType> {
*/ */
handleNavigationParams() { handleNavigationParams() {
const {props} = this; const {props} = this;
if (props.route.params != null) { if (props.route.params.data) {
if (props.route.params.data != null) { this.finalDataset = props.route.params.data;
this.finalDataset = props.route.params.data; // reset params to prevent infinite loop
// reset params to prevent infinite loop props.navigation.dispatch(CommonActions.setParams({data: null}));
props.navigation.dispatch(CommonActions.setParams({data: null})); props.navigation.setOptions({
props.navigation.setOptions({ headerTitle: this.finalDataset.title,
headerTitle: this.finalDataset.title, });
});
}
} }
} }
render(): React.Node { render() {
const {props} = this; if (!this.finalDataset) {
const { return null;
containerPaddingTop, }
scrollIndicatorInsetTop,
onScroll,
} = props.collapsibleStack;
return ( return (
<CardList <CardList dataset={this.finalDataset.content} isHorizontal={false} />
dataset={this.finalDataset.content}
isHorizontal={false}
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20,
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/>
); );
} }
} }
export default withCollapsible(ServicesSectionScreen); export default ServicesSectionScreen;

View file

@ -17,8 +17,6 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {StackNavigationProp} from '@react-navigation/stack'; import {StackNavigationProp} from '@react-navigation/stack';
import WebViewScreen from '../../components/Screens/WebViewScreen'; import WebViewScreen from '../../components/Screens/WebViewScreen';
@ -26,21 +24,24 @@ import AvailableWebsites from '../../constants/AvailableWebsites';
import BasicLoadingScreen from '../../components/Screens/BasicLoadingScreen'; import BasicLoadingScreen from '../../components/Screens/BasicLoadingScreen';
type PropsType = { type PropsType = {
navigation: StackNavigationProp, navigation: StackNavigationProp<any>;
route: {params: {host: string, path: string | null, title: string}}, route: {params: {host: string; path: string | null; title: string}};
}; };
const ENABLE_MOBILE_STRING = `<meta name="viewport" content="width=device-width, initial-scale=1.0">`; const ENABLE_MOBILE_STRING =
'<meta name="viewport" content="width=device-width, initial-scale=1.0">';
const AVAILABLE_ROOMS_STYLE = `<style>body,body>.container2{padding-top:0;width:100%}b,body>.container2>h1,body>.container2>h3,br,header{display:none}.table-bordered td,.table-bordered th{border:none;border-right:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.table{padding:0;margin:0;width:200%;max-width:200%;display:block}tbody{display:block;width:100%}thead{display:block;width:100%}.table tbody tr,tbody tr[bgcolor],thead tr{width:100%;display:inline-flex}.table tbody td,.table thead td[colspan]{padding:0;flex:1;height:50px;margin:0}.table tbody td[bgcolor=white],.table thead td,.table>tbody>tr>td:nth-child(1){flex:0 0 150px;height:50px}</style>`; const AVAILABLE_ROOMS_STYLE =
const BIB_STYLE = `<style>.hero-unit,.navbar,footer{display:none}.hero-unit-form,.hero-unit2,.hero-unit3{background-color:#fff;box-shadow:none;padding:0;margin:0}.hero-unit-form h4{font-size:2rem;line-height:2rem}.btn{font-size:1.5rem;line-height:1.5rem;padding:20px}.btn-danger{background-image:none;background-color:#be1522}.table{font-size:.8rem}.table td{padding:0;height:18.2333px;border:none;border-bottom:1px solid #c1c1c1}.table td[style="max-width:55px;"]{max-width:110px!important}.table-bordered{min-width:50px}th{height:50px}.table-bordered{border-collapse:collapse}</style>`; '<style>body,body>.container2{padding-top:0;width:100%}b,body>.container2>h1,body>.container2>h3,br,header{display:none}.table-bordered td,.table-bordered th{border:none;border-right:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.table{padding:0;margin:0;width:200%;max-width:200%;display:block}tbody{display:block;width:100%}thead{display:block;width:100%}.table tbody tr,tbody tr[bgcolor],thead tr{width:100%;display:inline-flex}.table tbody td,.table thead td[colspan]{padding:0;flex:1;height:50px;margin:0}.table tbody td[bgcolor=white],.table thead td,.table>tbody>tr>td:nth-child(1){flex:0 0 150px;height:50px}</style>';
const BIB_STYLE =
'<style>.hero-unit,.navbar,footer{display:none}.hero-unit-form,.hero-unit2,.hero-unit3{background-color:#fff;box-shadow:none;padding:0;margin:0}.hero-unit-form h4{font-size:2rem;line-height:2rem}.btn{font-size:1.5rem;line-height:1.5rem;padding:20px}.btn-danger{background-image:none;background-color:#be1522}.table{font-size:.8rem}.table td{padding:0;height:18.2333px;border:none;border-bottom:1px solid #c1c1c1}.table td[style="max-width:55px;"]{max-width:110px!important}.table-bordered{min-width:50px}th{height:50px}.table-bordered{border-collapse:collapse}</style>';
const BIB_BACK_BUTTON = const BIB_BACK_BUTTON =
`<div style='width: 100%; display: flex'>` + "<div style='width: 100%; display: flex'>" +
`<a style='margin: auto' href='${AvailableWebsites.websites.BIB}'>` + `<a style='margin: auto' href='${AvailableWebsites.websites.BIB}'>` +
`<button id='customBackButton' class='btn btn-primary'>Retour</button>` + "<button id='customBackButton' class='btn btn-primary'>Retour</button>" +
`</a>` + '</a>' +
`</div>`; '</div>';
class WebsiteScreen extends React.Component<PropsType> { class WebsiteScreen extends React.Component<PropsType> {
fullUrl: string; fullUrl: string;
@ -53,6 +54,8 @@ class WebsiteScreen extends React.Component<PropsType> {
constructor(props: PropsType) { constructor(props: PropsType) {
super(props); super(props);
this.fullUrl = '';
this.host = '';
props.navigation.addListener('focus', this.onScreenFocus); props.navigation.addListener('focus', this.onScreenFocus);
this.injectedJS = {}; this.injectedJS = {};
this.customPaddingFunctions = {}; this.customPaddingFunctions = {};
@ -63,7 +66,7 @@ class WebsiteScreen extends React.Component<PropsType> {
this.injectedJS[AvailableWebsites.websites.BIB] = this.injectedJS[AvailableWebsites.websites.BIB] =
`document.querySelector('head').innerHTML += '${ENABLE_MOBILE_STRING}';` + `document.querySelector('head').innerHTML += '${ENABLE_MOBILE_STRING}';` +
`document.querySelector('head').innerHTML += '${BIB_STYLE}';` + `document.querySelector('head').innerHTML += '${BIB_STYLE}';` +
`if ($(".hero-unit-form").length > 0 && $("#customBackButton").length === 0)` + 'if ($(".hero-unit-form").length > 0 && $("#customBackButton").length === 0)' +
`$(".hero-unit-form").append("${BIB_BACK_BUTTON}");true;`; `$(".hero-unit-form").append("${BIB_BACK_BUTTON}");true;`;
this.customPaddingFunctions[AvailableWebsites.websites.BLUEMIND] = ( this.customPaddingFunctions[AvailableWebsites.websites.BLUEMIND] = (
@ -72,7 +75,7 @@ class WebsiteScreen extends React.Component<PropsType> {
return ( return (
`$('head').append('${ENABLE_MOBILE_STRING}');` + `$('head').append('${ENABLE_MOBILE_STRING}');` +
`$('.minwidth').css('top', ${padding}` + `$('.minwidth').css('top', ${padding}` +
`$('#mailview-bottom').css('min-height', 500);` "$('#mailview-bottom').css('min-height', 500);"
); );
}; };
this.customPaddingFunctions[AvailableWebsites.websites.WIKETUD] = ( this.customPaddingFunctions[AvailableWebsites.websites.WIKETUD] = (
@ -103,20 +106,26 @@ class WebsiteScreen extends React.Component<PropsType> {
if (this.host != null && path != null) { if (this.host != null && path != null) {
path = path.replace(this.host, ''); path = path.replace(this.host, '');
this.fullUrl = this.host + path; this.fullUrl = this.host + path;
} else this.fullUrl = this.host; } else {
this.fullUrl = this.host;
}
if (title != null) navigation.setOptions({title}); if (title != null) {
navigation.setOptions({title});
}
} }
} }
render(): React.Node { render() {
const {navigation} = this.props; const {navigation} = this.props;
let injectedJavascript = ''; let injectedJavascript = '';
let customPadding = null; let customPadding = null;
if (this.host != null && this.injectedJS[this.host] != null) if (this.host != null && this.injectedJS[this.host] != null) {
injectedJavascript = this.injectedJS[this.host]; injectedJavascript = this.injectedJS[this.host];
if (this.host != null && this.customPaddingFunctions[this.host] != null) }
if (this.host != null && this.customPaddingFunctions[this.host] != null) {
customPadding = this.customPaddingFunctions[this.host]; customPadding = this.customPaddingFunctions[this.host];
}
if (this.fullUrl != null) { if (this.fullUrl != null) {
return ( return (