Compare commits

..

No commits in common. "4f80cadfed22277c8c26bb618da272f843d81359" and "cbb0624189418faed4dfa6ce49c6b07c3624c429" have entirely different histories.

10 changed files with 120 additions and 134 deletions

View file

@ -1,60 +1,46 @@
import * as React from 'react'; import * as React from 'react';
import {View} from "react-native";
import {withTheme} from 'react-native-paper'; import {withTheme} from 'react-native-paper';
import {Agenda} from "react-native-calendars"; import {Agenda} from "react-native-calendars";
type Props = {
theme: Object,
}
/** /**
* Abstraction layer for Agenda component, using custom configuration * Abstraction layer for Agenda component, using custom configuration
*
* @param props Props to pass to the element. Must specify an onRef prop to get an Agenda ref.
* @return {*}
*/ */
class CustomAgenda extends React.Component<Props> { function CustomAgenda(props) {
const {colors} = props.theme;
getAgenda() { return (
return <Agenda <Agenda
{...this.props} {...props}
ref={this.props.onRef} ref={props.onRef}
theme={{ theme={{
backgroundColor: this.props.theme.colors.agendaBackgroundColor, backgroundColor: colors.agendaBackgroundColor,
calendarBackground: this.props.theme.colors.background, calendarBackground: colors.background,
textSectionTitleColor: this.props.theme.colors.agendaDayTextColor, textSectionTitleColor: colors.agendaDayTextColor,
selectedDayBackgroundColor: this.props.theme.colors.primary, selectedDayBackgroundColor: colors.primary,
selectedDayTextColor: '#ffffff', selectedDayTextColor: '#ffffff',
todayTextColor: this.props.theme.colors.primary, todayTextColor: colors.primary,
dayTextColor: this.props.theme.colors.text, dayTextColor: colors.text,
textDisabledColor: this.props.theme.colors.agendaDayTextColor, textDisabledColor: colors.agendaDayTextColor,
dotColor: this.props.theme.colors.primary, dotColor: colors.primary,
selectedDotColor: '#ffffff', selectedDotColor: '#ffffff',
arrowColor: 'orange', arrowColor: 'orange',
monthTextColor: this.props.theme.colors.primary, monthTextColor: colors.primary,
indicatorColor: this.props.theme.colors.primary, indicatorColor: colors.primary,
textDayFontWeight: '300', textDayFontWeight: '300',
textMonthFontWeight: 'bold', textMonthFontWeight: 'bold',
textDayHeaderFontWeight: '300', textDayHeaderFontWeight: '300',
textDayFontSize: 16, textDayFontSize: 16,
textMonthFontSize: 16, textMonthFontSize: 16,
textDayHeaderFontSize: 16, textDayHeaderFontSize: 16,
agendaDayTextColor: this.props.theme.colors.agendaDayTextColor, agendaDayTextColor: colors.agendaDayTextColor,
agendaDayNumColor: this.props.theme.colors.agendaDayTextColor, agendaDayNumColor: colors.agendaDayTextColor,
agendaTodayColor: this.props.theme.colors.primary, agendaTodayColor: colors.primary,
agendaKnobColor: this.props.theme.colors.primary, agendaKnobColor: colors.primary,
}} }}
/>; />
} );
render() {
// Completely recreate the component on theme change to force theme reload
if (this.props.theme.dark)
return (
<View style={{flex: 1}}>
{this.getAgenda()}
</View>
);
else
return this.getAgenda();
}
} }
export default withTheme(CustomAgenda); export default withTheme(CustomAgenda);

View file

@ -1,44 +0,0 @@
import * as React from 'react';
import {View} from "react-native";
import {withTheme} from 'react-native-paper';
import HTML from "react-native-render-html";
import {Linking} from "expo";
type Props = {
theme: Object,
html: string,
}
/**
* Abstraction layer for Agenda component, using custom configuration
*/
class CustomHTML extends React.Component<Props> {
openWebLink = (event, link) => {
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
};
getHTML() {
// Surround description with div to allow text styling if the description is not html
return <HTML html={"<div>" + this.props.html + "</div>"}
tagsStyles={{
p: {color: this.props.theme.colors.text},
div: {color: this.props.theme.colors.text}
}}
onLinkPress={this.openWebLink}/>;
}
render() {
// Completely recreate the component on theme change to force theme reload
if (this.props.theme.dark)
return (
<View>
{this.getHTML()}
</View>
);
else
return this.getHTML();
}
}
export default withTheme(CustomHTML);

View file

@ -2,10 +2,10 @@
import * as React from 'react'; import * as React from 'react';
import {StyleSheet, View} from "react-native"; import {StyleSheet, View} from "react-native";
import HTML from "react-native-render-html";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {Avatar, Button, Card} from 'react-native-paper'; import {Avatar, Button, Card, withTheme} from 'react-native-paper';
import {getFormattedEventTime, isDescriptionEmpty} from "../../utils/Planning"; import {getFormattedEventTime, isDescriptionEmpty} from "../../utils/Planning";
import CustomHTML from "../Custom/CustomHTML";
/** /**
* Component used to display an event preview if an event is available * Component used to display an event preview if an event is available
@ -13,7 +13,8 @@ import CustomHTML from "../Custom/CustomHTML";
* @param props Props to pass to the component * @param props Props to pass to the component
* @return {*} * @return {*}
*/ */
function PreviewEventDashboardItem(props : Object) { function PreviewEventDashboardItem(props) {
const {colors} = props.theme;
const isEmpty = props.event === undefined const isEmpty = props.event === undefined
? true ? true
: isDescriptionEmpty(props.event['description']); : isDescriptionEmpty(props.event['description']);
@ -42,7 +43,12 @@ function PreviewEventDashboardItem(props : Object) {
/>} />}
{!isEmpty ? {!isEmpty ?
<Card.Content style={styles.content}> <Card.Content style={styles.content}>
<CustomHTML html={props.event['description']}/> <HTML html={"<div>" + props.event['description'] + "</div>"}
tagsStyles={{
p: {color: colors.text,},
div: {color: colors.text},
}}/>
</Card.Content> : null} </Card.Content> : null}
<Card.Actions style={styles.actions}> <Card.Actions style={styles.actions}>
@ -76,4 +82,4 @@ const styles = StyleSheet.create({
} }
}); });
export default PreviewEventDashboardItem; export default withTheme(PreviewEventDashboardItem);

View file

@ -234,11 +234,13 @@ type Props = {
class TabNavigator extends React.Component<Props>{ class TabNavigator extends React.Component<Props>{
createHomeStackComponent: Object; createHomeStackComponent: Object;
colors: Object;
defaultRoute: string; defaultRoute: string;
constructor(props) { constructor(props) {
super(props); super(props);
this.colors = props.theme.colors;
this.defaultRoute = AsyncStorageManager.getInstance().preferences.defaultStartScreen.current.toLowerCase(); this.defaultRoute = AsyncStorageManager.getInstance().preferences.defaultStartScreen.current.toLowerCase();
if (props.defaultRoute !== null) if (props.defaultRoute !== null)
@ -251,7 +253,7 @@ class TabNavigator extends React.Component<Props>{
return ( return (
<Tab.Navigator <Tab.Navigator
initialRouteName={this.defaultRoute} initialRouteName={this.defaultRoute}
barStyle={{backgroundColor: this.props.theme.colors.surface}} barStyle={{backgroundColor: this.colors.surface}}
screenOptions={({route}) => ({ screenOptions={({route}) => ({
tabBarIcon: ({focused, color, size}) => { tabBarIcon: ({focused, color, size}) => {
let icon = TAB_ICONS[route.name]; let icon = TAB_ICONS[route.name];
@ -260,8 +262,8 @@ class TabNavigator extends React.Component<Props>{
return <MaterialCommunityIcons name={icon} color={color} size={26}/>; return <MaterialCommunityIcons name={icon} color={color} size={26}/>;
}, },
})} })}
activeColor={this.props.theme.colors.primary} activeColor={this.colors.primary}
inactiveColor={this.props.theme.colors.tabIcon} inactiveColor={this.colors.tabIcon}
> >
<Tab.Screen <Tab.Screen
name="proximo" name="proximo"

View file

@ -2,11 +2,12 @@
import * as React from 'react'; import * as React from 'react';
import {ScrollView, View} from 'react-native'; import {ScrollView, View} from 'react-native';
import HTML from "react-native-render-html";
import {Linking} from "expo";
import {Avatar, Card, Chip, Paragraph, withTheme} from 'react-native-paper'; import {Avatar, Card, Chip, Paragraph, withTheme} from 'react-native-paper';
import ImageModal from 'react-native-image-modal'; import ImageModal from 'react-native-image-modal';
import i18n from "i18n-js"; import i18n from "i18n-js";
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen"; import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
import CustomHTML from "../../../components/Custom/CustomHTML";
type Props = { type Props = {
navigation: Object, navigation: Object,
@ -17,6 +18,10 @@ type State = {
imageModalVisible: boolean, imageModalVisible: boolean,
}; };
function openWebLink(event, link) {
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
}
/** /**
* Class defining a club event information page. * Class defining a club event information page.
* If called with data and categories navigation parameters, will use those to display the data. * If called with data and categories navigation parameters, will use those to display the data.
@ -141,7 +146,12 @@ class ClubDisplayScreen extends React.Component<Props, State> {
{data.description !== null ? {data.description !== null ?
// Surround description with div to allow text styling if the description is not html // Surround description with div to allow text styling if the description is not html
<Card.Content> <Card.Content>
<CustomHTML html={data.description}/> <HTML html={"<div>" + data.description + "</div>"}
tagsStyles={{
p: {color: this.colors.text,},
div: {color: this.colors.text}
}}
onLinkPress={openWebLink}/>
</Card.Content> </Card.Content>
: <View/>} : <View/>}
{this.getManagersRender(data.responsibles)} {this.getManagersRender(data.responsibles)}

View file

@ -24,12 +24,15 @@ class ProfileScreen extends React.Component<Props, State> {
dialogVisible: false, dialogVisible: false,
}; };
colors: Object;
data: Object; data: Object;
flatListData: Array<Object>; flatListData: Array<Object>;
constructor() { constructor(props) {
super(); super(props);
this.colors = props.theme.colors;
this.flatListData = [ this.flatListData = [
{id: '0'}, {id: '0'},
{id: '1'}, {id: '1'},
@ -101,6 +104,18 @@ class ProfileScreen extends React.Component<Props, State> {
: i18n.t("profileScreen.noData"); : i18n.t("profileScreen.noData");
} }
/**
* Gets the color depending on the value.
*
* @param field The field to get the color for
* @return {*}
*/
getFieldColor(field: ?string) {
return this.isFieldAvailable(field)
? this.colors.text
: this.colors.textDisabled;
}
/** /**
* Gets a list item showing personal information * Gets a list item showing personal information
* *
@ -109,17 +124,15 @@ class ProfileScreen extends React.Component<Props, State> {
* @return {*} * @return {*}
*/ */
getPersonalListItem(field: ?string, icon: string) { getPersonalListItem(field: ?string, icon: string) {
let title = this.isFieldAvailable(field) ? this.getFieldValue(field) : ':(';
let subtitle = this.isFieldAvailable(field) ? '' : this.getFieldValue(field);
return ( return (
<List.Item <List.Item
title={title} title={this.getFieldValue(field)}
description={subtitle}
left={props => <List.Icon left={props => <List.Icon
{...props} {...props}
icon={icon} icon={icon}
color={this.isFieldAvailable(field) ? undefined : this.props.theme.colors.textDisabled} color={this.getFieldColor(field)}
/>} />}
titleStyle={{color: this.getFieldColor(field)}}
/> />
); );
} }
@ -138,7 +151,7 @@ class ProfileScreen extends React.Component<Props, State> {
left={(props) => <Avatar.Icon left={(props) => <Avatar.Icon
{...props} {...props}
icon="account" icon="account"
color={this.props.theme.colors.primary} color={this.colors.primary}
style={styles.icon} style={styles.icon}
/>} />}
/> />
@ -156,7 +169,7 @@ class ProfileScreen extends React.Component<Props, State> {
<Button <Button
icon="account-edit" icon="account-edit"
mode="contained" mode="contained"
onPress={() => openBrowser(this.data.link, this.props.theme.colors.primary)} onPress={() => openBrowser(this.data.link, this.colors.primary)}
style={styles.editButton}> style={styles.editButton}>
{i18n.t("profileScreen.editInformation")} {i18n.t("profileScreen.editInformation")}
</Button> </Button>
@ -180,7 +193,7 @@ class ProfileScreen extends React.Component<Props, State> {
left={(props) => <Avatar.Icon left={(props) => <Avatar.Icon
{...props} {...props}
icon="account-group" icon="account-group"
color={this.props.theme.colors.primary} color={this.colors.primary}
style={styles.icon} style={styles.icon}
/>} />}
/> />
@ -206,7 +219,7 @@ class ProfileScreen extends React.Component<Props, State> {
left={(props) => <Avatar.Icon left={(props) => <Avatar.Icon
{...props} {...props}
icon="credit-card" icon="credit-card"
color={this.props.theme.colors.primary} color={this.colors.primary}
style={styles.icon} style={styles.icon}
/>} />}
/> />
@ -230,7 +243,7 @@ class ProfileScreen extends React.Component<Props, State> {
title={state ? i18n.t("profileScreen.membershipPayed") : i18n.t("profileScreen.membershipNotPayed")} title={state ? i18n.t("profileScreen.membershipPayed") : i18n.t("profileScreen.membershipNotPayed")}
left={props => <List.Icon left={props => <List.Icon
{...props} {...props}
color={state ? this.props.theme.colors.success : this.props.theme.colors.danger} color={state ? this.colors.success : this.colors.danger}
icon={state ? 'check' : 'close'} icon={state ? 'check' : 'close'}
/>} />}
/> />
@ -257,7 +270,7 @@ class ProfileScreen extends React.Component<Props, State> {
let icon = (props) => <List.Icon {...props} icon="chevron-right"/>; let icon = (props) => <List.Icon {...props} icon="chevron-right"/>;
if (item.is_manager) { if (item.is_manager) {
description = i18n.t("profileScreen.isManager"); description = i18n.t("profileScreen.isManager");
icon = (props) => <List.Icon {...props} icon="star" color={this.props.theme.colors.primary}/>; icon = (props) => <List.Icon {...props} icon="star" color={this.colors.primary}/>;
} }
return <List.Item return <List.Item
title={item.name} title={item.name}

View file

@ -83,7 +83,7 @@ class HomeScreen extends React.Component<Props> {
if (this.props.route.params.nextScreen !== undefined && this.props.route.params.nextScreen !== null) { if (this.props.route.params.nextScreen !== undefined && this.props.route.params.nextScreen !== null) {
this.props.navigation.navigate(this.props.route.params.nextScreen, this.props.route.params.data); this.props.navigation.navigate(this.props.route.params.nextScreen, this.props.route.params.data);
// reset params to prevent infinite loop // reset params to prevent infinite loop
this.props.navigation.dispatch(CommonActions.setParams({nextScreen: null})); this.props.navigation.dispatch(CommonActions.setParams({ nextScreen: null }));
} }
} }
}; };
@ -137,7 +137,7 @@ class HomeScreen extends React.Component<Props> {
id: SECTIONS_ID[1] id: SECTIONS_ID[1]
} }
]; ];
}; }
/** /**
* Generates the dataset associated to the dashboard to be displayed in the FlatList as a section * Generates the dataset associated to the dashboard to be displayed in the FlatList as a section
@ -349,7 +349,7 @@ class HomeScreen extends React.Component<Props> {
let displayEvent = this.getDisplayEvent(futureEvents); let displayEvent = this.getDisplayEvent(futureEvents);
const clickContainerAction = () => this.props.navigation.navigate('planning'); const clickContainerAction = () => this.props.navigation.navigate('planning');
const clickPreviewAction = () => this.props.navigation.navigate('home-planning-information', {data: displayEvent}); const clickPreviewAction = () => this.props.navigation.navigate('planning-information', {data: displayEvent});
return ( return (
<DashboardItem <DashboardItem
@ -469,13 +469,13 @@ class HomeScreen extends React.Component<Props> {
const nav = this.props.navigation; const nav = this.props.navigation;
return ( return (
<View> <View>
<WebSectionList <WebSectionList
createDataset={this.createDataset} createDataset={this.createDataset}
navigation={nav} navigation={nav}
autoRefreshTime={REFRESH_TIME} autoRefreshTime={REFRESH_TIME}
refreshOnFocus={true} refreshOnFocus={true}
fetchUrl={DATA_URL} fetchUrl={DATA_URL}
renderItem={this.getRenderItem}/> renderItem={this.getRenderItem}/>
<FAB <FAB
style={styles.fab} style={styles.fab}
icon="qrcode-scan" icon="qrcode-scan"

View file

@ -2,6 +2,8 @@
import * as React from 'react'; import * as React from 'react';
import {ScrollView, View} from 'react-native'; import {ScrollView, View} from 'react-native';
import HTML from "react-native-render-html";
import {Linking} from "expo";
import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning'; import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
import {Card, withTheme} from 'react-native-paper'; import {Card, withTheme} from 'react-native-paper';
import DateManager from "../../managers/DateManager"; import DateManager from "../../managers/DateManager";
@ -9,7 +11,6 @@ import ImageModal from 'react-native-image-modal';
import BasicLoadingScreen from "../../components/Custom/BasicLoadingScreen"; import BasicLoadingScreen from "../../components/Custom/BasicLoadingScreen";
import {apiRequest} from "../../utils/WebData"; import {apiRequest} from "../../utils/WebData";
import ErrorView from "../../components/Custom/ErrorView"; import ErrorView from "../../components/Custom/ErrorView";
import CustomHTML from "../../components/Custom/CustomHTML";
type Props = { type Props = {
navigation: Object, navigation: Object,
@ -20,6 +21,10 @@ type State = {
loading: boolean loading: boolean
}; };
function openWebLink(event, link) {
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
}
const CLUB_INFO_PATH = "event/info"; const CLUB_INFO_PATH = "event/info";
/** /**
@ -106,8 +111,14 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
: <View/>} : <View/>}
{this.displayData.description !== null ? {this.displayData.description !== null ?
// Surround description with div to allow text styling if the description is not html
<Card.Content> <Card.Content>
<CustomHTML html={this.displayData.description}/> <HTML html={"<div>" + this.displayData.description + "</div>"}
tagsStyles={{
p: {color: this.colors.text,},
div: {color: this.colors.text}
}}
onLinkPress={openWebLink}/>
</Card.Content> </Card.Content>
: <View/>} : <View/>}
</ScrollView> </ScrollView>
@ -120,7 +131,7 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
else if (this.errorCode === 0) else if (this.errorCode === 0)
return this.getContent(); return this.getContent();
else else
return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>; return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>;
} }
} }

View file

@ -40,7 +40,7 @@ const AGENDA_MONTH_SPAN = 3;
/** /**
* Class defining the app's planning screen * Class defining the app's planning screen
*/ */
class PlanningScreen extends React.Component<Props, State> { export default class PlanningScreen extends React.Component<Props, State> {
agendaRef: Object; agendaRef: Object;
@ -226,15 +226,10 @@ class PlanningScreen extends React.Component<Props, State> {
); );
} }
componentDidUpdate(prevProps: Props, prevState: State, prevContext: *): * {
console.log('coucou');
}
render() { render() {
// console.log("rendering PlanningScreen"); // console.log("rendering PlanningScreen");
return ( return (
<CustomAgenda <CustomAgenda
{...this.props}
// the list of items that have to be displayed in agenda. If you want to render item as empty date // the list of items that have to be displayed in agenda. If you want to render item as empty date
// the value of date key kas to be an empty array []. If there exists no value for date key it is // the value of date key kas to be an empty array []. If there exists no value for date key it is
// considered that the date in question is not yet loaded // considered that the date in question is not yet loaded
@ -264,5 +259,3 @@ class PlanningScreen extends React.Component<Props, State> {
); );
} }
} }
export default PlanningScreen;

View file

@ -4,10 +4,9 @@ import * as React from 'react';
import {FlatList, Image, Platform, ScrollView, View} from "react-native"; import {FlatList, Image, Platform, ScrollView, View} from "react-native";
import i18n from "i18n-js"; import i18n from "i18n-js";
import CustomModal from "../../components/Custom/CustomModal"; import CustomModal from "../../components/Custom/CustomModal";
import {RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper"; import {IconButton, RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper";
import {stringMatchQuery} from "../../utils/Search"; import {stringMatchQuery} from "../../utils/Search";
import ProximoListItem from "../../components/Lists/ProximoListItem"; import ProximoListItem from "../../components/Lists/ProximoListItem";
import HeaderButton from "../../components/Custom/HeaderButton";
function sortPrice(a, b) { function sortPrice(a, b) {
return a.price - b.price; return a.price - b.price;
@ -38,7 +37,6 @@ const LIST_ITEM_HEIGHT = 84;
type Props = { type Props = {
navigation: Object, navigation: Object,
route: Object, route: Object,
theme: Object,
} }
type State = { type State = {
@ -56,6 +54,8 @@ class ProximoListScreen extends React.Component<Props, State> {
listData: Array<Object>; listData: Array<Object>;
shouldFocusSearchBar: boolean; shouldFocusSearchBar: boolean;
colors: Object;
constructor(props) { constructor(props) {
super(props); super(props);
this.listData = this.props.route.params['data']['data']; this.listData = this.props.route.params['data']['data'];
@ -65,6 +65,8 @@ class ProximoListScreen extends React.Component<Props, State> {
currentSortMode: 3, currentSortMode: 3,
modalCurrentDisplayItem: null, modalCurrentDisplayItem: null,
}; };
this.colors = props.theme.colors;
} }
@ -102,7 +104,14 @@ class ProximoListScreen extends React.Component<Props, State> {
* @return {*} * @return {*}
*/ */
getSortMenuButton = () => { getSortMenuButton = () => {
return <HeaderButton icon="sort" onPress={this.onSortMenuPress}/>; return (
<IconButton
icon="sort"
color={this.colors.text}
size={26}
onPress={this.onSortMenuPress}
/>
);
}; };
/** /**
@ -155,11 +164,11 @@ class ProximoListScreen extends React.Component<Props, State> {
getStockColor(availableStock: number) { getStockColor(availableStock: number) {
let color: string; let color: string;
if (availableStock > 3) if (availableStock > 3)
color = this.props.theme.colors.success; color = this.colors.success;
else if (availableStock > 0) else if (availableStock > 0)
color = this.props.theme.colors.warning; color = this.colors.warning;
else else
color = this.props.theme.colors.danger; color = this.colors.danger;
return color; return color;
} }