Update home screens to use TypeScript

This commit is contained in:
Arnaud Vergnet 2020-09-22 22:52:35 +02:00
parent 38afbf02a3
commit c198a40148
4 changed files with 102 additions and 128 deletions

View file

@ -23,9 +23,9 @@ import {Dimensions, Image, View} from 'react-native';
import * as Animatable from 'react-native-animatable';
type PropsType = {
image: string | null;
onPress: () => void | null;
badgeCount: number | null;
image?: string | number;
onPress?: () => void;
badgeCount?: number;
};
/**
@ -50,7 +50,7 @@ function SmallDashboardItem(props: PropsType) {
}}>
{image ? (
<Image
source={{uri: image}}
source={typeof image === 'string' ? {uri: image} : image}
style={{
width: '80%',
height: '80%',

View file

@ -17,11 +17,9 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
// @flow
import * as React from 'react';
import {Linking, Image} from 'react-native';
import {Card, Text, withTheme} from 'react-native-paper';
import {Card, Text} from 'react-native-paper';
import Autolink from 'react-native-autolink';
import {StackNavigationProp} from '@react-navigation/stack';
import MaterialHeaderButtons, {
@ -31,12 +29,14 @@ import CustomTabBar from '../../components/Tabbar/CustomTabBar';
import type {FeedItemType} from './HomeScreen';
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
import ImageGalleryButton from '../../components/Media/ImageGalleryButton';
import NewsSourcesConstants from '../../constants/NewsSourcesConstants';
import NewsSourcesConstants, {
AvailablePages,
} from '../../constants/NewsSourcesConstants';
import type {NewsSourceType} from '../../constants/NewsSourcesConstants';
type PropsType = {
navigation: StackNavigationProp,
route: {params: {data: FeedItemType, date: string}},
navigation: StackNavigationProp<any>;
route: {params: {data: FeedItemType; date: string}};
};
/**
@ -72,7 +72,7 @@ class FeedItemScreen extends React.Component<PropsType> {
*
* @returns {*}
*/
getHeaderButton = (): React.Node => {
getHeaderButton = () => {
return (
<MaterialHeaderButtons>
<Item
@ -85,20 +85,16 @@ class FeedItemScreen extends React.Component<PropsType> {
);
};
render(): React.Node {
const {navigation} = this.props;
const hasImage =
this.displayData.image !== '' && this.displayData.image != null;
render() {
const pageSource: NewsSourceType =
NewsSourcesConstants[this.displayData.page_id];
NewsSourcesConstants[this.displayData.page_id as AvailablePages];
return (
<CollapsibleScrollView style={{margin: 5}} hasTab>
<Card.Title
title={pageSource.name}
subtitle={this.date}
left={(): React.Node => (
left={() => (
<Image
size={48}
source={pageSource.icon}
style={{
width: 48,
@ -107,9 +103,8 @@ class FeedItemScreen extends React.Component<PropsType> {
/>
)}
/>
{hasImage ? (
{this.displayData.image ? (
<ImageGalleryButton
navigation={navigation}
images={[{url: this.displayData.image}]}
style={{
width: 250,
@ -124,6 +119,7 @@ class FeedItemScreen extends React.Component<PropsType> {
<Autolink
text={this.displayData.message}
hashtag="facebook"
// @ts-ignore
component={Text}
/>
) : null}
@ -133,4 +129,4 @@ class FeedItemScreen extends React.Component<PropsType> {
}
}
export default withTheme(FeedItemScreen);
export default FeedItemScreen;

View file

@ -17,10 +17,8 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
// @flow
import * as React from 'react';
import {FlatList} from 'react-native';
import {FlatList, NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
import i18n from 'i18n-js';
import {ActivityIndicator, Headline, withTheme} from 'react-native-paper';
import {CommonActions} from '@react-navigation/native';
@ -38,7 +36,6 @@ import MaterialHeaderButtons, {
Item,
} from '../../components/Overrides/CustomHeaderButton';
import AnimatedFAB from '../../components/Animations/AnimatedFAB';
import type {CustomThemeType} from '../../managers/ThemeManager';
import ConnectionManager from '../../managers/ConnectionManager';
import LogoutDialog from '../../components/Amicale/LogoutDialog';
import AsyncStorageManager from '../../managers/AsyncStorageManager';
@ -59,40 +56,40 @@ const SECTIONS_ID = ['dashboard', 'news_feed'];
const REFRESH_TIME = 1000 * 20; // Refresh every 20 seconds
export type FeedItemType = {
id: string,
message: string,
url: string,
image: string | null,
video: string | null,
link: string | null,
time: number,
page_id: string,
id: string;
message: string;
url: string;
image: string | null;
video: string | null;
link: string | null;
time: number;
page_id: string;
};
export type FullDashboardType = {
today_menu: Array<{[key: string]: {...}}>,
proximo_articles: number,
available_dryers: number,
available_washers: number,
today_events: Array<PlanningEventType>,
available_tutorials: number,
today_menu: Array<{[key: string]: object}>;
proximo_articles: number;
available_dryers: number;
available_washers: number;
today_events: Array<PlanningEventType>;
available_tutorials: number;
};
type RawNewsFeedType = {[key: string]: Array<FeedItemType>};
type RawDashboardType = {
news_feed: RawNewsFeedType,
dashboard: FullDashboardType,
news_feed: RawNewsFeedType;
dashboard: FullDashboardType;
};
type PropsType = {
navigation: StackNavigationProp,
route: {params: {nextScreen: string, data: {...}}},
theme: CustomThemeType,
navigation: StackNavigationProp<any>;
route: {params: {nextScreen: string; data: object}};
theme: ReactNativePaper.Theme;
};
type StateType = {
dialogVisible: boolean,
dialogVisible: boolean;
};
/**
@ -103,10 +100,12 @@ class HomeScreen extends React.Component<PropsType, StateType> {
b.time - a.time;
static generateNewsFeed(rawFeed: RawNewsFeedType): Array<FeedItemType> {
const finalFeed = [];
const finalFeed: Array<FeedItemType> = [];
Object.keys(rawFeed).forEach((key: string) => {
const category: Array<FeedItemType> | null = rawFeed[key];
if (category != null && category.length > 0) finalFeed.push(...category);
if (category != null && category.length > 0) {
finalFeed.push(...category);
}
});
finalFeed.sort(HomeScreen.sortFeedTime);
return finalFeed;
@ -164,7 +163,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
*
* @returns {*}
*/
getHeaderButton = (): React.Node => {
getHeaderButton = () => {
const {props} = this;
let onPressLog = (): void =>
props.navigation.navigate('login', {nextScreen: 'profile'});
@ -201,7 +200,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
* @param content
* @return {*}
*/
getDashboardEvent(content: Array<PlanningEventType>): React.Node {
getDashboardEvent(content: Array<PlanningEventType>) {
const futureEvents = getFutureEvents(content);
const displayEvent = getDisplayEvent(futureEvents);
// const clickPreviewAction = () =>
@ -221,30 +220,14 @@ class HomeScreen extends React.Component<PropsType, StateType> {
);
}
/**
* Gets a dashboard item with action buttons
*
* @returns {*}
*/
getDashboardActions(): React.Node {
const {props} = this;
return (
<ActionsDashBoardItem
navigation={props.navigation}
isLoggedIn={this.isLoggedIn}
/>
);
}
/**
* Gets a dashboard item with a row of shortcut buttons.
*
* @param content
* @return {*}
*/
getDashboardRow(content: Array<ServiceItemType | null>): React.Node {
getDashboardRow(content: Array<ServiceItemType | null>) {
return (
// $FlowFixMe
<FlatList
data={content}
renderItem={this.getDashboardRowRenderItem}
@ -265,12 +248,8 @@ class HomeScreen extends React.Component<PropsType, StateType> {
* @param item
* @returns {*}
*/
getDashboardRowRenderItem = ({
item,
}: {
item: ServiceItemType | null,
}): React.Node => {
if (item != null)
getDashboardRowRenderItem = ({item}: {item: ServiceItemType | null}) => {
if (item != null) {
return (
<SmallDashboardItem
image={item.image}
@ -278,11 +257,12 @@ class HomeScreen extends React.Component<PropsType, StateType> {
badgeCount={
this.currentDashboard != null && item.badgeFunction != null
? item.badgeFunction(this.currentDashboard)
: null
: undefined
}
/>
);
return <SmallDashboardItem image={null} onPress={null} badgeCount={null} />;
}
return <SmallDashboardItem />;
};
/**
@ -291,15 +271,8 @@ class HomeScreen extends React.Component<PropsType, StateType> {
* @param item The feed item to display
* @return {*}
*/
getFeedItem(item: FeedItemType): React.Node {
const {props} = this;
return (
<FeedItem
navigation={props.navigation}
item={item}
height={FEED_ITEM_HEIGHT}
/>
);
getFeedItem(item: FeedItemType) {
return <FeedItem item={item} height={FEED_ITEM_HEIGHT} />;
}
/**
@ -309,20 +282,19 @@ class HomeScreen extends React.Component<PropsType, StateType> {
* @param section The current section
* @return {*}
*/
getRenderItem = ({item}: {item: FeedItemType}): React.Node =>
this.getFeedItem(item);
getRenderItem = ({item}: {item: FeedItemType}) => this.getFeedItem(item);
getRenderSectionHeader = (
data: {
section: {
data: Array<{...}>,
title: string,
},
data: Array<object>;
title: string;
};
},
isLoading: boolean,
): React.Node => {
) => {
const {props} = this;
if (data.section.data.length > 0)
if (data.section.data.length > 0) {
return (
<Headline
style={{
@ -333,6 +305,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
{data.section.title}
</Headline>
);
}
return (
<View>
<Headline
@ -367,12 +340,14 @@ class HomeScreen extends React.Component<PropsType, StateType> {
);
};
getListHeader = (fetchedData: RawDashboardType): React.Node => {
getListHeader = (fetchedData: RawDashboardType) => {
let dashboard = null;
if (fetchedData != null) dashboard = fetchedData.dashboard;
if (fetchedData != null) {
dashboard = fetchedData.dashboard;
}
return (
<Animatable.View animation="fadeInDown" duration={500} useNativeDriver>
{this.getDashboardActions()}
<ActionsDashBoardItem />
{this.getDashboardRow(this.dashboardManager.getCurrentDashboard())}
{this.getDashboardEvent(
dashboard == null ? [] : dashboard.today_events,
@ -418,20 +393,22 @@ class HomeScreen extends React.Component<PropsType, StateType> {
fetchedData: RawDashboardType | null,
isLoading: boolean,
): Array<{
title: string,
data: [] | Array<FeedItemType>,
id: string,
title: string;
data: [] | Array<FeedItemType>;
id: string;
}> => {
// fetchedData = DATA;
if (fetchedData != null) {
if (fetchedData.news_feed != null)
if (fetchedData.news_feed != null) {
this.currentNewFeed = HomeScreen.generateNewsFeed(
fetchedData.news_feed,
);
if (fetchedData.dashboard != null)
}
if (fetchedData.dashboard != null) {
this.currentDashboard = fetchedData.dashboard;
}
}
if (this.currentNewFeed.length > 0)
if (this.currentNewFeed.length > 0) {
return [
{
title: i18n.t('screens.home.feedTitle'),
@ -439,6 +416,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
id: SECTIONS_ID[1],
},
];
}
return [
{
title: isLoading
@ -455,8 +433,10 @@ class HomeScreen extends React.Component<PropsType, StateType> {
props.navigation.navigate('planning');
};
onScroll = (event: SyntheticEvent<EventTarget>) => {
if (this.fabRef.current != null) this.fabRef.current.onScroll(event);
onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
if (this.fabRef.current) {
this.fabRef.current.onScroll(event);
}
};
/**
@ -470,7 +450,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
});
};
render(): React.Node {
render() {
const {props, state} = this;
return (
<View style={{flex: 1}}>
@ -521,7 +501,6 @@ class HomeScreen extends React.Component<PropsType, StateType> {
onPress={this.openScanner}
/>
<LogoutDialog
navigation={props.navigation}
visible={state.dialogVisible}
onDismiss={this.hideDisconnectDialog}
/>

View file

@ -17,11 +17,9 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
// @flow
import * as React from 'react';
import {Linking, Platform, StyleSheet, View} from 'react-native';
import {Button, Text, withTheme} from 'react-native-paper';
import {Button, Text} from 'react-native-paper';
import {RNCamera} from 'react-native-camera';
import {BarcodeMask} from '@nartc/react-native-barcode-mask';
import i18n from 'i18n-js';
@ -34,11 +32,11 @@ import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
import MascotPopup from '../../components/Mascot/MascotPopup';
type StateType = {
hasPermission: boolean,
scanned: boolean,
dialogVisible: boolean,
mascotDialogVisible: boolean,
loading: boolean,
hasPermission: boolean;
scanned: boolean;
dialogVisible: boolean;
mascotDialogVisible: boolean;
loading: boolean;
};
const styles = StyleSheet.create({
@ -54,9 +52,11 @@ const styles = StyleSheet.create({
},
});
class ScannerScreen extends React.Component<null, StateType> {
type PermissionResults = 'unavailable' | 'denied' | 'blocked' | 'granted';
class ScannerScreen extends React.Component<{}, StateType> {
constructor() {
super();
super({});
this.state = {
hasPermission: false,
scanned: false,
@ -75,7 +75,7 @@ class ScannerScreen extends React.Component<null, StateType> {
*
* @returns {*}
*/
getPermissionScreen(): React.Node {
getPermissionScreen() {
return (
<View style={{marginLeft: 10, marginRight: 10}}>
<Text>{i18n.t('screens.scanner.permissions.error')}</Text>
@ -101,15 +101,13 @@ class ScannerScreen extends React.Component<null, StateType> {
*
* @returns {*}
*/
getScanner(): React.Node {
getScanner() {
const {state} = this;
return (
<RNCamera
onBarCodeRead={state.scanned ? null : this.onCodeScanned}
onBarCodeRead={state.scanned ? undefined : this.onCodeScanned}
type={RNCamera.Constants.Type.back}
barCodeScannerSettings={{
barCodeTypes: [RNCamera.Constants.BarCodeType.qr],
}}
barCodeTypes={['qr']}
style={StyleSheet.absoluteFill}
captureAudio={false}>
<BarcodeMask
@ -128,9 +126,11 @@ class ScannerScreen extends React.Component<null, StateType> {
* Requests permission to use the camera
*/
requestPermissions = () => {
if (Platform.OS === 'android')
if (Platform.OS === 'android') {
request(PERMISSIONS.ANDROID.CAMERA).then(this.updatePermissionStatus);
else request(PERMISSIONS.IOS.CAMERA).then(this.updatePermissionStatus);
} else {
request(PERMISSIONS.IOS.CAMERA).then(this.updatePermissionStatus);
}
};
/**
@ -138,7 +138,7 @@ class ScannerScreen extends React.Component<null, StateType> {
*
* @param result
*/
updatePermissionStatus = (result: RESULTS) => {
updatePermissionStatus = (result: PermissionResults) => {
this.setState({
hasPermission: result === RESULTS.GRANTED,
});
@ -147,7 +147,6 @@ class ScannerScreen extends React.Component<null, StateType> {
/**
* Shows a dialog indicating the user the scanned code was invalid
*/
// eslint-disable-next-line react/sort-comp
showErrorDialog() {
this.setState({
dialogVisible: true,
@ -199,14 +198,15 @@ class ScannerScreen extends React.Component<null, StateType> {
* @param data The scanned value
*/
onCodeScanned = ({data}: {data: string}) => {
if (!URLHandler.isUrlValid(data)) this.showErrorDialog();
else {
if (!URLHandler.isUrlValid(data)) {
this.showErrorDialog();
} else {
this.showOpeningDialog();
Linking.openURL(data);
}
};
render(): React.Node {
render() {
const {state} = this;
return (
<View
@ -228,7 +228,6 @@ class ScannerScreen extends React.Component<null, StateType> {
message={i18n.t('screens.scanner.mascotDialog.message')}
icon="camera-iris"
buttons={{
action: null,
cancel: {
message: i18n.t('screens.scanner.mascotDialog.button'),
icon: 'check',
@ -253,4 +252,4 @@ class ScannerScreen extends React.Component<null, StateType> {
}
}
export default withTheme(ScannerScreen);
export default ScannerScreen;