Update Home base components to use TypeScript
This commit is contained in:
parent
140bcf3675
commit
e4530ded18
11 changed files with 480 additions and 549 deletions
|
@ -1,75 +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 {List, withTheme} from 'react-native-paper';
|
|
||||||
import {View} from 'react-native';
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
|
||||||
import type {CustomThemeType} from '../../managers/ThemeManager';
|
|
||||||
import type {ListIconPropsType} from '../../constants/PaperStyles';
|
|
||||||
|
|
||||||
type PropsType = {
|
|
||||||
navigation: StackNavigationProp,
|
|
||||||
theme: CustomThemeType,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ActionsDashBoardItem extends React.Component<PropsType> {
|
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
|
||||||
const {props} = this;
|
|
||||||
return nextProps.theme.dark !== props.theme.dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): React.Node {
|
|
||||||
const {navigation} = this.props;
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<List.Item
|
|
||||||
title={i18n.t('screens.feedback.homeButtonTitle')}
|
|
||||||
description={i18n.t('screens.feedback.homeButtonSubtitle')}
|
|
||||||
left={(props: ListIconPropsType): React.Node => (
|
|
||||||
<List.Icon
|
|
||||||
color={props.color}
|
|
||||||
style={props.style}
|
|
||||||
icon="comment-quote"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
right={(props: ListIconPropsType): React.Node => (
|
|
||||||
<List.Icon
|
|
||||||
color={props.color}
|
|
||||||
style={props.style}
|
|
||||||
icon="chevron-right"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
onPress={(): void => navigation.navigate('feedback')}
|
|
||||||
style={{
|
|
||||||
paddingTop: 0,
|
|
||||||
paddingBottom: 0,
|
|
||||||
marginLeft: 10,
|
|
||||||
marginRight: 10,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withTheme(ActionsDashBoardItem);
|
|
59
src/components/Home/ActionsDashboardItem.tsx
Normal file
59
src/components/Home/ActionsDashboardItem.tsx
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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 {List} from 'react-native-paper';
|
||||||
|
import {View} from 'react-native';
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
import {useNavigation} from '@react-navigation/native';
|
||||||
|
|
||||||
|
function ActionsDashBoardItem() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<List.Item
|
||||||
|
title={i18n.t('screens.feedback.homeButtonTitle')}
|
||||||
|
description={i18n.t('screens.feedback.homeButtonSubtitle')}
|
||||||
|
left={(props) => (
|
||||||
|
<List.Icon
|
||||||
|
color={props.color}
|
||||||
|
style={props.style}
|
||||||
|
icon="comment-quote"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
right={(props) => (
|
||||||
|
<List.Icon
|
||||||
|
color={props.color}
|
||||||
|
style={props.style}
|
||||||
|
icon="chevron-right"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
onPress={(): void => navigation.navigate('feedback')}
|
||||||
|
style={{
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 0,
|
||||||
|
marginLeft: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionsDashBoardItem;
|
|
@ -1,116 +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 {
|
|
||||||
Avatar,
|
|
||||||
Card,
|
|
||||||
Text,
|
|
||||||
TouchableRipple,
|
|
||||||
withTheme,
|
|
||||||
} from 'react-native-paper';
|
|
||||||
import {StyleSheet, View} from 'react-native';
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
import type {CustomThemeType} from '../../managers/ThemeManager';
|
|
||||||
import type {CardTitleIconPropsType} from '../../constants/PaperStyles';
|
|
||||||
|
|
||||||
type PropsType = {
|
|
||||||
eventNumber: number,
|
|
||||||
clickAction: () => void,
|
|
||||||
theme: CustomThemeType,
|
|
||||||
children?: React.Node,
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
card: {
|
|
||||||
width: 'auto',
|
|
||||||
marginLeft: 10,
|
|
||||||
marginRight: 10,
|
|
||||||
marginTop: 10,
|
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component used to display a dashboard item containing a preview event
|
|
||||||
*/
|
|
||||||
class EventDashBoardItem extends React.Component<PropsType> {
|
|
||||||
static defaultProps = {
|
|
||||||
children: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
|
||||||
const {props} = this;
|
|
||||||
return (
|
|
||||||
nextProps.theme.dark !== props.theme.dark ||
|
|
||||||
nextProps.eventNumber !== props.eventNumber
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): React.Node {
|
|
||||||
const {props} = this;
|
|
||||||
const {colors} = props.theme;
|
|
||||||
const isAvailable = props.eventNumber > 0;
|
|
||||||
const iconColor = isAvailable ? colors.planningColor : colors.textDisabled;
|
|
||||||
const textColor = isAvailable ? colors.text : colors.textDisabled;
|
|
||||||
let subtitle;
|
|
||||||
if (isAvailable) {
|
|
||||||
subtitle = (
|
|
||||||
<Text>
|
|
||||||
<Text style={{fontWeight: 'bold'}}>{props.eventNumber}</Text>
|
|
||||||
<Text>
|
|
||||||
{props.eventNumber > 1
|
|
||||||
? i18n.t('screens.home.dashboard.todayEventsSubtitlePlural')
|
|
||||||
: i18n.t('screens.home.dashboard.todayEventsSubtitle')}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
} else subtitle = i18n.t('screens.home.dashboard.todayEventsSubtitleNA');
|
|
||||||
return (
|
|
||||||
<Card style={styles.card}>
|
|
||||||
<TouchableRipple style={{flex: 1}} onPress={props.clickAction}>
|
|
||||||
<View>
|
|
||||||
<Card.Title
|
|
||||||
title={i18n.t('screens.home.dashboard.todayEventsTitle')}
|
|
||||||
titleStyle={{color: textColor}}
|
|
||||||
subtitle={subtitle}
|
|
||||||
subtitleStyle={{color: textColor}}
|
|
||||||
left={(iconProps: CardTitleIconPropsType): React.Node => (
|
|
||||||
<Avatar.Icon
|
|
||||||
icon="calendar-range"
|
|
||||||
color={iconColor}
|
|
||||||
size={iconProps.size}
|
|
||||||
style={styles.avatar}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Card.Content>{props.children}</Card.Content>
|
|
||||||
</View>
|
|
||||||
</TouchableRipple>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withTheme(EventDashBoardItem);
|
|
104
src/components/Home/EventDashboardItem.tsx
Normal file
104
src/components/Home/EventDashboardItem.tsx
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
Avatar,
|
||||||
|
Card,
|
||||||
|
Text,
|
||||||
|
TouchableRipple,
|
||||||
|
useTheme,
|
||||||
|
} from 'react-native-paper';
|
||||||
|
import {StyleSheet, View} from 'react-native';
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
|
eventNumber: number;
|
||||||
|
clickAction: () => void;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
card: {
|
||||||
|
width: 'auto',
|
||||||
|
marginLeft: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
marginTop: 10,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used to display a dashboard item containing a preview event
|
||||||
|
*/
|
||||||
|
function EventDashBoardItem(props: PropsType) {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isAvailable = props.eventNumber > 0;
|
||||||
|
const iconColor = isAvailable
|
||||||
|
? theme.colors.planningColor
|
||||||
|
: theme.colors.textDisabled;
|
||||||
|
const textColor = isAvailable ? theme.colors.text : theme.colors.textDisabled;
|
||||||
|
let subtitle;
|
||||||
|
if (isAvailable) {
|
||||||
|
subtitle = (
|
||||||
|
<Text>
|
||||||
|
<Text style={{fontWeight: 'bold'}}>{props.eventNumber}</Text>
|
||||||
|
<Text>
|
||||||
|
{props.eventNumber > 1
|
||||||
|
? i18n.t('screens.home.dashboard.todayEventsSubtitlePlural')
|
||||||
|
: i18n.t('screens.home.dashboard.todayEventsSubtitle')}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
subtitle = i18n.t('screens.home.dashboard.todayEventsSubtitleNA');
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Card style={styles.card}>
|
||||||
|
<TouchableRipple style={{flex: 1}} onPress={props.clickAction}>
|
||||||
|
<View>
|
||||||
|
<Card.Title
|
||||||
|
title={i18n.t('screens.home.dashboard.todayEventsTitle')}
|
||||||
|
titleStyle={{color: textColor}}
|
||||||
|
subtitle={subtitle}
|
||||||
|
subtitleStyle={{color: textColor}}
|
||||||
|
left={(iconProps) => (
|
||||||
|
<Avatar.Icon
|
||||||
|
icon="calendar-range"
|
||||||
|
color={iconColor}
|
||||||
|
size={iconProps.size}
|
||||||
|
style={styles.avatar}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Card.Content>{props.children}</Card.Content>
|
||||||
|
</View>
|
||||||
|
</TouchableRipple>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const areEqual = (prevProps: PropsType, nextProps: PropsType): boolean => {
|
||||||
|
return nextProps.eventNumber !== prevProps.eventNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(EventDashBoardItem, areEqual);
|
|
@ -1,139 +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 {Button, Card, Text, TouchableRipple} from 'react-native-paper';
|
|
||||||
import {Image, View} from 'react-native';
|
|
||||||
import Autolink from 'react-native-autolink';
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
import {StackNavigationProp} from '@react-navigation/stack';
|
|
||||||
import type {FeedItemType} from '../../screens/Home/HomeScreen';
|
|
||||||
import NewsSourcesConstants from '../../constants/NewsSourcesConstants';
|
|
||||||
import type {NewsSourceType} from '../../constants/NewsSourcesConstants';
|
|
||||||
import ImageGalleryButton from '../Media/ImageGalleryButton';
|
|
||||||
|
|
||||||
type PropsType = {
|
|
||||||
navigation: StackNavigationProp,
|
|
||||||
item: FeedItemType,
|
|
||||||
height: number,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component used to display a feed item
|
|
||||||
*/
|
|
||||||
class FeedItem extends React.Component<PropsType> {
|
|
||||||
/**
|
|
||||||
* Converts a dateString using Unix Timestamp to a formatted date
|
|
||||||
*
|
|
||||||
* @param dateString {string} The Unix Timestamp representation of a date
|
|
||||||
* @return {string} The formatted output date
|
|
||||||
*/
|
|
||||||
static getFormattedDate(dateString: number): string {
|
|
||||||
const date = new Date(dateString * 1000);
|
|
||||||
return date.toLocaleString();
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate(): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onPress = () => {
|
|
||||||
const {item, navigation} = this.props;
|
|
||||||
navigation.navigate('feed-information', {
|
|
||||||
data: item,
|
|
||||||
date: FeedItem.getFormattedDate(item.time),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render(): React.Node {
|
|
||||||
const {item, height, navigation} = this.props;
|
|
||||||
const image = item.image !== '' && item.image != null ? item.image : null;
|
|
||||||
const pageSource: NewsSourceType = NewsSourcesConstants[item.page_id];
|
|
||||||
const cardMargin = 10;
|
|
||||||
const cardHeight = height - 2 * cardMargin;
|
|
||||||
const imageSize = 250;
|
|
||||||
const titleHeight = 80;
|
|
||||||
const actionsHeight = 60;
|
|
||||||
const textHeight =
|
|
||||||
image != null
|
|
||||||
? cardHeight - titleHeight - actionsHeight - imageSize
|
|
||||||
: cardHeight - titleHeight - actionsHeight;
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
style={{
|
|
||||||
margin: cardMargin,
|
|
||||||
height: cardHeight,
|
|
||||||
}}>
|
|
||||||
<TouchableRipple style={{flex: 1}} onPress={this.onPress}>
|
|
||||||
<View>
|
|
||||||
<Card.Title
|
|
||||||
title={pageSource.name}
|
|
||||||
subtitle={FeedItem.getFormattedDate(item.time)}
|
|
||||||
left={(): React.Node => (
|
|
||||||
<Image
|
|
||||||
size={48}
|
|
||||||
source={pageSource.icon}
|
|
||||||
style={{
|
|
||||||
width: 48,
|
|
||||||
height: 48,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
style={{height: titleHeight}}
|
|
||||||
/>
|
|
||||||
{image != null ? (
|
|
||||||
<ImageGalleryButton
|
|
||||||
navigation={navigation}
|
|
||||||
images={[{url: image}]}
|
|
||||||
style={{
|
|
||||||
width: imageSize,
|
|
||||||
height: imageSize,
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<Card.Content>
|
|
||||||
{item.message !== undefined ? (
|
|
||||||
<Autolink
|
|
||||||
text={item.message}
|
|
||||||
hashtag="facebook"
|
|
||||||
component={Text}
|
|
||||||
style={{height: textHeight}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Card.Content>
|
|
||||||
<Card.Actions style={{height: actionsHeight}}>
|
|
||||||
<Button
|
|
||||||
onPress={this.onPress}
|
|
||||||
icon="plus"
|
|
||||||
style={{marginLeft: 'auto'}}>
|
|
||||||
{i18n.t('screens.home.dashboard.seeMore')}
|
|
||||||
</Button>
|
|
||||||
</Card.Actions>
|
|
||||||
</View>
|
|
||||||
</TouchableRipple>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FeedItem;
|
|
128
src/components/Home/FeedItem.tsx
Normal file
128
src/components/Home/FeedItem.tsx
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* 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 {Button, Card, Text, TouchableRipple} from 'react-native-paper';
|
||||||
|
import {Image, View} from 'react-native';
|
||||||
|
import Autolink from 'react-native-autolink';
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
import type {FeedItemType} from '../../screens/Home/HomeScreen';
|
||||||
|
import NewsSourcesConstants, {
|
||||||
|
AvailablePages,
|
||||||
|
} from '../../constants/NewsSourcesConstants';
|
||||||
|
import type {NewsSourceType} from '../../constants/NewsSourcesConstants';
|
||||||
|
import ImageGalleryButton from '../Media/ImageGalleryButton';
|
||||||
|
import {useNavigation} from '@react-navigation/native';
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
|
item: FeedItemType;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a dateString using Unix Timestamp to a formatted date
|
||||||
|
*
|
||||||
|
* @param dateString {string} The Unix Timestamp representation of a date
|
||||||
|
* @return {string} The formatted output date
|
||||||
|
*/
|
||||||
|
function getFormattedDate(dateString: number): string {
|
||||||
|
const date = new Date(dateString * 1000);
|
||||||
|
return date.toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used to display a feed item
|
||||||
|
*/
|
||||||
|
function FeedItem(props: PropsType) {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const onPress = () => {
|
||||||
|
navigation.navigate('feed-information', {
|
||||||
|
data: item,
|
||||||
|
date: getFormattedDate(props.item.time),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const {item, height} = props;
|
||||||
|
const image = item.image !== '' && item.image != null ? item.image : null;
|
||||||
|
const pageSource: NewsSourceType =
|
||||||
|
NewsSourcesConstants[item.page_id as AvailablePages];
|
||||||
|
const cardMargin = 10;
|
||||||
|
const cardHeight = height - 2 * cardMargin;
|
||||||
|
const imageSize = 250;
|
||||||
|
const titleHeight = 80;
|
||||||
|
const actionsHeight = 60;
|
||||||
|
const textHeight =
|
||||||
|
image != null
|
||||||
|
? cardHeight - titleHeight - actionsHeight - imageSize
|
||||||
|
: cardHeight - titleHeight - actionsHeight;
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
margin: cardMargin,
|
||||||
|
height: cardHeight,
|
||||||
|
}}>
|
||||||
|
<TouchableRipple style={{flex: 1}} onPress={onPress}>
|
||||||
|
<View>
|
||||||
|
<Card.Title
|
||||||
|
title={pageSource.name}
|
||||||
|
subtitle={getFormattedDate(item.time)}
|
||||||
|
left={() => (
|
||||||
|
<Image
|
||||||
|
source={pageSource.icon}
|
||||||
|
style={{
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
style={{height: titleHeight}}
|
||||||
|
/>
|
||||||
|
{image != null ? (
|
||||||
|
<ImageGalleryButton
|
||||||
|
images={[{url: image}]}
|
||||||
|
style={{
|
||||||
|
width: imageSize,
|
||||||
|
height: imageSize,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Card.Content>
|
||||||
|
{item.message !== undefined ? (
|
||||||
|
<Autolink<typeof Text>
|
||||||
|
text={item.message}
|
||||||
|
hashtag="facebook"
|
||||||
|
component={Text}
|
||||||
|
style={{height: textHeight}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</Card.Content>
|
||||||
|
<Card.Actions style={{height: actionsHeight}}>
|
||||||
|
<Button onPress={onPress} icon="plus" style={{marginLeft: 'auto'}}>
|
||||||
|
{i18n.t('screens.home.dashboard.seeMore')}
|
||||||
|
</Button>
|
||||||
|
</Card.Actions>
|
||||||
|
</View>
|
||||||
|
</TouchableRipple>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(FeedItem, () => true);
|
|
@ -1,113 +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 {StyleSheet, View} from 'react-native';
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
import {Avatar, Button, Card, TouchableRipple} from 'react-native-paper';
|
|
||||||
import {getTimeOnlyString, isDescriptionEmpty} from '../../utils/Planning';
|
|
||||||
import CustomHTML from '../Overrides/CustomHTML';
|
|
||||||
import type {PlanningEventType} from '../../utils/Planning';
|
|
||||||
|
|
||||||
type PropsType = {
|
|
||||||
event?: PlanningEventType | null,
|
|
||||||
clickAction: () => void,
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
card: {
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
maxHeight: 150,
|
|
||||||
overflow: 'hidden',
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginTop: 'auto',
|
|
||||||
flexDirection: 'row',
|
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component used to display an event preview if an event is available
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line react/prefer-stateless-function
|
|
||||||
class PreviewEventDashboardItem extends React.Component<PropsType> {
|
|
||||||
static defaultProps = {
|
|
||||||
event: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
render(): React.Node {
|
|
||||||
const {props} = this;
|
|
||||||
const {event} = props;
|
|
||||||
const isEmpty =
|
|
||||||
event == null ? true : isDescriptionEmpty(event.description);
|
|
||||||
|
|
||||||
if (event != null) {
|
|
||||||
const hasImage = event.logo !== '' && event.logo != null;
|
|
||||||
const getImage = (): React.Node => (
|
|
||||||
<Avatar.Image
|
|
||||||
source={{uri: event.logo}}
|
|
||||||
size={50}
|
|
||||||
style={styles.avatar}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Card style={styles.card} elevation={3}>
|
|
||||||
<TouchableRipple style={{flex: 1}} onPress={props.clickAction}>
|
|
||||||
<View>
|
|
||||||
{hasImage ? (
|
|
||||||
<Card.Title
|
|
||||||
title={event.title}
|
|
||||||
subtitle={getTimeOnlyString(event.date_begin)}
|
|
||||||
left={getImage}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Card.Title
|
|
||||||
title={event.title}
|
|
||||||
subtitle={getTimeOnlyString(event.date_begin)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!isEmpty ? (
|
|
||||||
<Card.Content style={styles.content}>
|
|
||||||
<CustomHTML html={event.description} />
|
|
||||||
</Card.Content>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<Card.Actions style={styles.actions}>
|
|
||||||
<Button icon="chevron-right">
|
|
||||||
{i18n.t('screens.home.dashboard.seeMore')}
|
|
||||||
</Button>
|
|
||||||
</Card.Actions>
|
|
||||||
</View>
|
|
||||||
</TouchableRipple>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PreviewEventDashboardItem;
|
|
93
src/components/Home/PreviewEventDashboardItem.tsx
Normal file
93
src/components/Home/PreviewEventDashboardItem.tsx
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* 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 {StyleSheet, View} from 'react-native';
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
import {Avatar, Button, Card, TouchableRipple} from 'react-native-paper';
|
||||||
|
import {getTimeOnlyString, isDescriptionEmpty} from '../../utils/Planning';
|
||||||
|
import CustomHTML from '../Overrides/CustomHTML';
|
||||||
|
import type {PlanningEventType} from '../../utils/Planning';
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
|
event?: PlanningEventType | null;
|
||||||
|
clickAction: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
card: {
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
maxHeight: 150,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginTop: 'auto',
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used to display an event preview if an event is available
|
||||||
|
*/
|
||||||
|
function PreviewEventDashboardItem(props: PropsType) {
|
||||||
|
const {event} = props;
|
||||||
|
const isEmpty = event == null ? true : isDescriptionEmpty(event.description);
|
||||||
|
|
||||||
|
if (event != null) {
|
||||||
|
const logo = event.logo;
|
||||||
|
const getImage = logo
|
||||||
|
? () => (
|
||||||
|
<Avatar.Image source={{uri: logo}} size={50} style={styles.avatar} />
|
||||||
|
)
|
||||||
|
: () => null;
|
||||||
|
return (
|
||||||
|
<Card style={styles.card} elevation={3}>
|
||||||
|
<TouchableRipple style={{flex: 1}} onPress={props.clickAction}>
|
||||||
|
<View>
|
||||||
|
<Card.Title
|
||||||
|
title={event.title}
|
||||||
|
subtitle={getTimeOnlyString(event.date_begin)}
|
||||||
|
left={getImage}
|
||||||
|
/>
|
||||||
|
{!isEmpty ? (
|
||||||
|
<Card.Content style={styles.content}>
|
||||||
|
<CustomHTML html={event.description} />
|
||||||
|
</Card.Content>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<Card.Actions style={styles.actions}>
|
||||||
|
<Button icon="chevron-right">
|
||||||
|
{i18n.t('screens.home.dashboard.seeMore')}
|
||||||
|
</Button>
|
||||||
|
</Card.Actions>
|
||||||
|
</View>
|
||||||
|
</TouchableRipple>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PreviewEventDashboardItem;
|
|
@ -1,106 +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 {Badge, TouchableRipple, withTheme} from 'react-native-paper';
|
|
||||||
import {Dimensions, Image, View} from 'react-native';
|
|
||||||
import * as Animatable from 'react-native-animatable';
|
|
||||||
import type {CustomThemeType} from '../../managers/ThemeManager';
|
|
||||||
|
|
||||||
type PropsType = {
|
|
||||||
image: string | null,
|
|
||||||
onPress: () => void | null,
|
|
||||||
badgeCount: number | null,
|
|
||||||
theme: CustomThemeType,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component used to render a small dashboard item
|
|
||||||
*/
|
|
||||||
class SmallDashboardItem extends React.Component<PropsType> {
|
|
||||||
itemSize: number;
|
|
||||||
|
|
||||||
constructor(props: PropsType) {
|
|
||||||
super(props);
|
|
||||||
this.itemSize = Dimensions.get('window').width / 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: PropsType): boolean {
|
|
||||||
const {props} = this;
|
|
||||||
return (
|
|
||||||
nextProps.theme.dark !== props.theme.dark ||
|
|
||||||
nextProps.badgeCount !== props.badgeCount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): React.Node {
|
|
||||||
const {props} = this;
|
|
||||||
return (
|
|
||||||
<TouchableRipple
|
|
||||||
onPress={props.onPress}
|
|
||||||
borderless
|
|
||||||
style={{
|
|
||||||
marginLeft: this.itemSize / 6,
|
|
||||||
marginRight: this.itemSize / 6,
|
|
||||||
}}>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
width: this.itemSize,
|
|
||||||
height: this.itemSize,
|
|
||||||
}}>
|
|
||||||
<Image
|
|
||||||
source={{uri: props.image}}
|
|
||||||
style={{
|
|
||||||
width: '80%',
|
|
||||||
height: '80%',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{props.badgeCount != null && props.badgeCount > 0 ? (
|
|
||||||
<Animatable.View
|
|
||||||
animation="zoomIn"
|
|
||||||
duration={300}
|
|
||||||
useNativeDriver
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
right: 0,
|
|
||||||
}}>
|
|
||||||
<Badge
|
|
||||||
style={{
|
|
||||||
backgroundColor: props.theme.colors.primary,
|
|
||||||
borderColor: props.theme.colors.background,
|
|
||||||
borderWidth: 2,
|
|
||||||
}}>
|
|
||||||
{props.badgeCount}
|
|
||||||
</Badge>
|
|
||||||
</Animatable.View>
|
|
||||||
) : null}
|
|
||||||
</View>
|
|
||||||
</TouchableRipple>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withTheme(SmallDashboardItem);
|
|
94
src/components/Home/SmallDashboardItem.tsx
Normal file
94
src/components/Home/SmallDashboardItem.tsx
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* 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 {Badge, TouchableRipple, useTheme} from 'react-native-paper';
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component used to render a small dashboard item
|
||||||
|
*/
|
||||||
|
function SmallDashboardItem(props: PropsType) {
|
||||||
|
const itemSize = Dimensions.get('window').width / 8;
|
||||||
|
const theme = useTheme();
|
||||||
|
const {image} = props;
|
||||||
|
return (
|
||||||
|
<TouchableRipple
|
||||||
|
onPress={props.onPress}
|
||||||
|
borderless
|
||||||
|
style={{
|
||||||
|
marginLeft: itemSize / 6,
|
||||||
|
marginRight: itemSize / 6,
|
||||||
|
}}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: itemSize,
|
||||||
|
height: itemSize,
|
||||||
|
}}>
|
||||||
|
{image ? (
|
||||||
|
<Image
|
||||||
|
source={{uri: image}}
|
||||||
|
style={{
|
||||||
|
width: '80%',
|
||||||
|
height: '80%',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginTop: 'auto',
|
||||||
|
marginBottom: 'auto',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{props.badgeCount != null && props.badgeCount > 0 ? (
|
||||||
|
<Animatable.View
|
||||||
|
animation="zoomIn"
|
||||||
|
duration={300}
|
||||||
|
useNativeDriver
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
}}>
|
||||||
|
<Badge
|
||||||
|
visible={true}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.colors.primary,
|
||||||
|
borderColor: theme.colors.background,
|
||||||
|
borderWidth: 2,
|
||||||
|
}}>
|
||||||
|
{props.badgeCount}
|
||||||
|
</Badge>
|
||||||
|
</Animatable.View>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
</TouchableRipple>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const areEqual = (prevProps: PropsType, nextProps: PropsType): boolean => {
|
||||||
|
return nextProps.badgeCount !== prevProps.badgeCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(SmallDashboardItem, areEqual);
|
|
@ -27,6 +27,8 @@ export type NewsSourceType = {
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AvailablePages = 'amicale.deseleves' | 'campus.insat';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
'amicale.deseleves': {
|
'amicale.deseleves': {
|
||||||
icon: ICON_AMICALE,
|
icon: ICON_AMICALE,
|
||||||
|
|
Loading…
Reference in a new issue