forked from vergnet/application-amicale
Update home screens to use TypeScript
This commit is contained in:
parent
38afbf02a3
commit
c198a40148
4 changed files with 102 additions and 128 deletions
|
@ -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%',
|
||||
|
|
|
@ -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;
|
|
@ -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}
|
||||
/>
|
|
@ -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;
|
Loading…
Reference in a new issue