Compare commits

..

No commits in common. "be33726e39e05353e1e203ade9c19d781be95263" and "560c3367593d87b9459c3d5e35773b004a63d9d3" have entirely different histories.

32 changed files with 531 additions and 522 deletions

View file

@ -95,10 +95,8 @@
}, },
"home": { "home": {
"title": "Campus", "title": "Campus",
"feedTitle": "Campus News", "feedTitle": "Amicale's News",
"feed": "Details", "feed": "Details",
"feedLoading": "Loading News",
"feedError": "Failed to load news",
"dashboard": { "dashboard": {
"seeMore": "Click to see more", "seeMore": "Click to see more",
"todayEventsTitle": "Today's events", "todayEventsTitle": "Today's events",

View file

@ -95,10 +95,8 @@
}, },
"home": { "home": {
"title": "Campus", "title": "Campus",
"feedTitle": "News du Campus", "feedTitle": "News de l'Amicale",
"feed": "Détails", "feed": "Détails",
"feedLoading": "Chargement des news",
"feedError": "Erreur de chargement des news",
"dashboard": { "dashboard": {
"seeMore": "Clique pour plus d'infos", "seeMore": "Clique pour plus d'infos",
"todayEventsTitle": "Événements aujourd'hui", "todayEventsTitle": "Événements aujourd'hui",

View file

@ -1,51 +0,0 @@
// @flow
import * as React from 'react';
import {withCollapsible} from "../../utils/withCollapsible";
import {Collapsible} from "react-navigation-collapsible";
import CustomTabBar from "../Tabbar/CustomTabBar";
export type CollapsibleComponentProps = {
children?: React.Node,
hasTab?: boolean,
onScroll?: (event: SyntheticEvent<EventTarget>) => void,
};
type Props = {
...CollapsibleComponentProps,
collapsibleStack: Collapsible,
component: any,
}
class CollapsibleComponent extends React.Component<Props> {
static defaultProps = {
hasTab: false,
}
onScroll = (event: SyntheticEvent<EventTarget>) => {
if (this.props.onScroll)
this.props.onScroll(event);
}
render() {
const Comp = this.props.component;
const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack;
return (
<Comp
{...this.props}
onScroll={onScrollWithListener(this.onScroll)}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: this.props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
>
{this.props.children}
</Comp>
);
}
}
export default withCollapsible(CollapsibleComponent);

View file

@ -1,26 +0,0 @@
// @flow
import * as React from 'react';
import {Animated} from "react-native";
import type {CollapsibleComponentProps} from "./CollapsibleComponent";
import CollapsibleComponent from "./CollapsibleComponent";
type Props = {
...CollapsibleComponentProps
}
class CollapsibleFlatList extends React.Component<Props> {
render() {
return (
<CollapsibleComponent
{...this.props}
component={Animated.FlatList}
>
{this.props.children}
</CollapsibleComponent>
);
}
}
export default CollapsibleFlatList;

View file

@ -1,26 +0,0 @@
// @flow
import * as React from 'react';
import {Animated} from "react-native";
import type {CollapsibleComponentProps} from "./CollapsibleComponent";
import CollapsibleComponent from "./CollapsibleComponent";
type Props = {
...CollapsibleComponentProps
}
class CollapsibleScrollView extends React.Component<Props> {
render() {
return (
<CollapsibleComponent
{...this.props}
component={Animated.ScrollView}
>
{this.props.children}
</CollapsibleComponent>
);
}
}
export default CollapsibleScrollView;

View file

@ -1,26 +0,0 @@
// @flow
import * as React from 'react';
import {Animated} from "react-native";
import type {CollapsibleComponentProps} from "./CollapsibleComponent";
import CollapsibleComponent from "./CollapsibleComponent";
type Props = {
...CollapsibleComponentProps
}
class CollapsibleSectionList extends React.Component<Props> {
render() {
return (
<CollapsibleComponent
{...this.props}
component={Animated.SectionList}
>
{this.props.children}
</CollapsibleComponent>
);
}
}
export default CollapsibleSectionList;

View file

@ -4,31 +4,28 @@ import * as React from 'react';
import {ERROR_TYPE, readData} from "../../utils/WebData"; import {ERROR_TYPE, readData} from "../../utils/WebData";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {Snackbar} from 'react-native-paper'; import {Snackbar} from 'react-native-paper';
import {RefreshControl, View} from "react-native"; import {Animated, RefreshControl, View} from "react-native";
import ErrorView from "./ErrorView"; import ErrorView from "./ErrorView";
import BasicLoadingScreen from "./BasicLoadingScreen"; import BasicLoadingScreen from "./BasicLoadingScreen";
import {withCollapsible} from "../../utils/withCollapsible"; import {withCollapsible} from "../../utils/withCollapsible";
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import CustomTabBar from "../Tabbar/CustomTabBar"; import CustomTabBar from "../Tabbar/CustomTabBar";
import {Collapsible} from "react-navigation-collapsible"; import {Collapsible} from "react-navigation-collapsible";
import {StackNavigationProp} from "@react-navigation/stack";
import CollapsibleSectionList from "../Collapsible/CollapsibleSectionList";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: { [key: string]: any },
fetchUrl: string, fetchUrl: string,
autoRefreshTime: number, autoRefreshTime: number,
refreshOnFocus: boolean, refreshOnFocus: boolean,
renderItem: (data: { [key: string]: any }) => React.Node, renderItem: (data: { [key: string]: any }) => React.Node,
createDataset: (data: { [key: string]: any } | null, isLoading?: boolean) => Array<Object>, createDataset: (data: { [key: string]: any }) => Array<Object>,
onScroll: (event: SyntheticEvent<EventTarget>) => void, onScroll: (event: SyntheticEvent<EventTarget>) => void,
collapsibleStack: Collapsible, collapsibleStack: Collapsible,
showError: boolean, showError: boolean,
itemHeight?: number, itemHeight?: number,
updateData?: number, updateData?: number,
renderListHeaderComponent?: (data: { [key: string]: any } | null) => React.Node, renderSectionHeader?: (data: { [key: string]: any }) => React.Node,
renderSectionHeader?: (data: { section: { [key: string]: any } }, isLoading?: boolean) => React.Node,
stickyHeader?: boolean, stickyHeader?: boolean,
} }
@ -56,6 +53,7 @@ class WebSectionList extends React.PureComponent<Props, State> {
showError: true, showError: true,
}; };
scrollRef: { current: null | Animated.SectionList };
refreshInterval: IntervalID; refreshInterval: IntervalID;
lastRefresh: Date | null; lastRefresh: Date | null;
@ -71,26 +69,31 @@ class WebSectionList extends React.PureComponent<Props, State> {
* Allows to detect when the screen is focused * Allows to detect when the screen is focused
*/ */
componentDidMount() { componentDidMount() {
this.props.navigation.addListener('focus', this.onScreenFocus); const onScreenFocus = this.onScreenFocus.bind(this);
this.props.navigation.addListener('blur', this.onScreenBlur); const onScreenBlur = this.onScreenBlur.bind(this);
this.lastRefresh = null; this.props.navigation.addListener('focus', onScreenFocus);
this.props.navigation.addListener('blur', onScreenBlur);
this.scrollRef = React.createRef();
this.onRefresh(); this.onRefresh();
this.lastRefresh = null;
} }
/** /**
* Refreshes data when focusing the screen and setup a refresh interval if asked to * Refreshes data when focusing the screen and setup a refresh interval if asked to
*/ */
onScreenFocus = () => { onScreenFocus() {
if (this.props.refreshOnFocus && this.lastRefresh) if (this.props.refreshOnFocus && this.lastRefresh)
this.onRefresh(); this.onRefresh();
if (this.props.autoRefreshTime > 0) if (this.props.autoRefreshTime > 0)
this.refreshInterval = setInterval(this.onRefresh, this.props.autoRefreshTime) this.refreshInterval = setInterval(this.onRefresh, this.props.autoRefreshTime)
// if (this.scrollRef.current) // Reset scroll to top
// this.scrollRef.current.getNode().scrollToLocation({animated:false, itemIndex:0, sectionIndex:0});
} }
/** /**
* Removes any interval on un-focus * Removes any interval on un-focus
*/ */
onScreenBlur = () => { onScreenBlur() {
clearInterval(this.refreshInterval); clearInterval(this.refreshInterval);
} }
@ -170,7 +173,7 @@ class WebSectionList extends React.PureComponent<Props, State> {
duration={500} duration={500}
useNativeDriver useNativeDriver
> >
{this.props.renderSectionHeader(data, this.state.refreshing)} {this.props.renderSectionHeader(data)}
</Animatable.View> </Animatable.View>
); );
} else } else
@ -202,12 +205,16 @@ class WebSectionList extends React.PureComponent<Props, State> {
render() { render() {
let dataset = []; let dataset = [];
if (this.state.fetchedData != null || (this.state.fetchedData == null && !this.props.showError)) { if (this.state.fetchedData != null || (this.state.fetchedData == null && !this.props.showError)) {
dataset = this.props.createDataset(this.state.fetchedData, this.state.refreshing); if (this.state.fetchedData == null)
dataset = this.props.createDataset({});
else
dataset = this.props.createDataset(this.state.fetchedData);
} }
const {containerPaddingTop} = this.props.collapsibleStack; const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack;
return ( return (
<View> <View>
<CollapsibleSectionList <Animated.SectionList
ref={this.scrollRef}
sections={dataset} sections={dataset}
extraData={this.props.updateData} extraData={this.props.updateData}
refreshControl={ refreshControl={
@ -221,9 +228,6 @@ class WebSectionList extends React.PureComponent<Props, State> {
renderItem={this.renderItem} renderItem={this.renderItem}
stickySectionHeadersEnabled={this.props.stickyHeader} stickySectionHeadersEnabled={this.props.stickyHeader}
style={{minHeight: '100%'}} style={{minHeight: '100%'}}
ListHeaderComponent={this.props.renderListHeaderComponent != null
? this.props.renderListHeaderComponent(this.state.fetchedData)
: null}
ListEmptyComponent={this.state.refreshing ListEmptyComponent={this.state.refreshing
? <BasicLoadingScreen/> ? <BasicLoadingScreen/>
: <ErrorView : <ErrorView
@ -232,8 +236,14 @@ class WebSectionList extends React.PureComponent<Props, State> {
onRefresh={this.onRefresh}/> onRefresh={this.onRefresh}/>
} }
getItemLayout={this.props.itemHeight != null ? this.itemLayout : undefined} getItemLayout={this.props.itemHeight != null ? this.itemLayout : undefined}
onScroll={this.onScroll} // Animations
hasTab={true} onScroll={onScrollWithListener(this.onScroll)}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/> />
<Snackbar <Snackbar
visible={this.state.snackbarVisible} visible={this.state.snackbarVisible}

View file

@ -28,13 +28,16 @@ import EquipmentLendScreen from "../screens/Amicale/Equipment/EquipmentRentScree
import EquipmentConfirmScreen from "../screens/Amicale/Equipment/EquipmentConfirmScreen"; import EquipmentConfirmScreen from "../screens/Amicale/Equipment/EquipmentConfirmScreen";
import DashboardEditScreen from "../screens/Other/Settings/DashboardEditScreen"; import DashboardEditScreen from "../screens/Other/Settings/DashboardEditScreen";
import GameStartScreen from "../screens/Game/screens/GameStartScreen"; import GameStartScreen from "../screens/Game/screens/GameStartScreen";
import GameEndScreen from "../screens/Game/screens/GameEndScreen";
const modalTransition = Platform.OS === 'ios' ? TransitionPresets.ModalPresentationIOS : TransitionPresets.ModalSlideFromBottomIOS; const modalTransition = Platform.OS === 'ios' ? TransitionPresets.ModalPresentationIOS : TransitionPresets.ModalSlideFromBottomIOS;
const screenTransition = TransitionPresets.SlideFromRightIOS;
const defaultScreenOptions = { const defaultScreenOptions = {
gestureEnabled: true, gestureEnabled: true,
cardOverlayEnabled: true, cardOverlayEnabled: true,
...TransitionPresets.SlideFromRightIOS, ...screenTransition,
}; };
@ -55,37 +58,48 @@ function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
title: i18n.t('screens.home.title'), title: i18n.t('screens.home.title'),
}} }}
/> />
{createScreenCollapsibleStack( <MainStack.Screen
"settings", name="settings"
MainStack, component={SettingsScreen}
SettingsScreen, options={{
i18n.t('screens.settings.title'))} title: i18n.t('screens.settings.title'),
{createScreenCollapsibleStack( }}
"dashboard-edit", />
MainStack, <MainStack.Screen
DashboardEditScreen, name="dashboard-edit"
i18n.t('screens.settings.dashboardEdit.title'))} component={DashboardEditScreen}
{createScreenCollapsibleStack( options={{
"about", title: i18n.t('screens.settings.dashboardEdit.title'),
MainStack, }}
AboutScreen, />
i18n.t('screens.about.title'))} <MainStack.Screen
{createScreenCollapsibleStack( name="about"
"dependencies", component={AboutScreen}
MainStack, options={{
AboutDependenciesScreen, title: i18n.t('screens.about.title'),
i18n.t('screens.about.libs'))} }}
{createScreenCollapsibleStack( />
"debug", <MainStack.Screen
MainStack, name="dependencies"
DebugScreen, component={AboutDependenciesScreen}
i18n.t('screens.about.debug'))} options={{
title: i18n.t('screens.about.libs')
{createScreenCollapsibleStack( }}
"game-start", />
MainStack, <MainStack.Screen
GameStartScreen, name="debug"
i18n.t('screens.game.title'))} component={DebugScreen}
options={{
title: i18n.t('screens.about.debug')
}}
/>
<MainStack.Screen
name="game-start"
component={GameStartScreen}
options={{
title: i18n.t("screens.game.title"),
}}
/>
<MainStack.Screen <MainStack.Screen
name="game-main" name="game-main"
component={GameMainScreen} component={GameMainScreen}
@ -93,91 +107,73 @@ function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
title: i18n.t("screens.game.title"), title: i18n.t("screens.game.title"),
}} }}
/> />
{createScreenCollapsibleStack( <MainStack.Screen
"login", name="game-end"
MainStack, component={GameEndScreen}
LoginScreen, options={{
i18n.t('screens.login.title'), title: i18n.t("screens.game.title"),
true, }}
{headerTintColor: "#fff"}, />
'transparent')} {createScreenCollapsibleStack("login", MainStack, LoginScreen, i18n.t('screens.login.title'),
true, {headerTintColor: "#fff"}, 'transparent')}
{getWebsiteStack("website", MainStack, WebsiteScreen, "")} {getWebsiteStack("website", MainStack, WebsiteScreen, "")}
{createScreenCollapsibleStack( {createScreenCollapsibleStack("self-menu", MainStack, SelfMenuScreen, i18n.t('screens.menu.title'))}
"self-menu", {createScreenCollapsibleStack("proximo", MainStack, ProximoMainScreen, i18n.t('screens.proximo.title'))}
MainStack,
SelfMenuScreen,
i18n.t('screens.menu.title'))}
{createScreenCollapsibleStack(
"proximo",
MainStack,
ProximoMainScreen,
i18n.t('screens.proximo.title'))}
{createScreenCollapsibleStack( {createScreenCollapsibleStack(
"proximo-list", "proximo-list",
MainStack, MainStack,
ProximoListScreen, ProximoListScreen,
i18n.t('screens.proximo.articleList'), i18n.t('screens.proximo.articleList'),
)}
{createScreenCollapsibleStack(
"proximo-about",
MainStack,
ProximoAboutScreen,
i18n.t('screens.proximo.title'),
true, true,
{...modalTransition}, {...screenTransition},
)} )}
<MainStack.Screen
name="proximo-about"
component={ProximoAboutScreen}
options={{
title: i18n.t('screens.proximo.title'),
...modalTransition,
}}
/>
{createScreenCollapsibleStack( {createScreenCollapsibleStack("profile", MainStack, ProfileScreen, i18n.t('screens.profile.title'))}
"profile", {createScreenCollapsibleStack("club-list", MainStack, ClubListScreen, i18n.t('screens.clubs.title'))}
MainStack, {createScreenCollapsibleStack("equipment-list", MainStack, EquipmentScreen, i18n.t('screens.equipment.title'))}
ProfileScreen, {createScreenCollapsibleStack("equipment-rent", MainStack, EquipmentLendScreen, i18n.t('screens.equipment.book'))}
i18n.t('screens.profile.title'))} {createScreenCollapsibleStack("equipment-confirm", MainStack, EquipmentConfirmScreen, i18n.t('screens.equipment.confirm'))}
{createScreenCollapsibleStack( <MainStack.Screen
"club-list", name="club-information"
MainStack, component={ClubDisplayScreen}
ClubListScreen, options={{
i18n.t('screens.clubs.title'))} title: i18n.t('screens.clubs.details'),
{createScreenCollapsibleStack( ...modalTransition,
"club-information", }}
MainStack, />
ClubDisplayScreen, <MainStack.Screen
i18n.t('screens.clubs.details'), name="club-about"
true, component={ClubAboutScreen}
{...modalTransition})} options={{
{createScreenCollapsibleStack( title: i18n.t('screens.clubs.title'),
"club-about", ...modalTransition,
MainStack, }}
ClubAboutScreen, />
i18n.t('screens.clubs.title'), <MainStack.Screen
true, name="vote"
{...modalTransition})} component={VoteScreen}
{createScreenCollapsibleStack( options={{
"equipment-list", title: i18n.t('screens.vote.title'),
MainStack, }}
EquipmentScreen, />
i18n.t('screens.equipment.title'))}
{createScreenCollapsibleStack( <MainStack.Screen
"equipment-rent", name="feedback"
MainStack, component={BugReportScreen}
EquipmentLendScreen, options={{
i18n.t('screens.equipment.book'))} title: i18n.t('screens.feedback.title'),
{createScreenCollapsibleStack( }}
"equipment-confirm", />
MainStack,
EquipmentConfirmScreen,
i18n.t('screens.equipment.confirm'))}
{createScreenCollapsibleStack(
"vote",
MainStack,
VoteScreen,
i18n.t('screens.vote.title'))}
{createScreenCollapsibleStack(
"feedback",
MainStack,
BugReportScreen,
i18n.t('screens.feedback.title'))}
</MainStack.Navigator> </MainStack.Navigator>
); );
} }

View file

@ -1,11 +1,10 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {FlatList} from "react-native";
import packageJson from '../../../package'; import packageJson from '../../../package';
import {List} from 'react-native-paper'; import {List} from 'react-native-paper';
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
import {View} from "react-native-animatable";
type listItem = { type listItem = {
name: string, name: string,
@ -60,8 +59,7 @@ export default class AboutDependenciesScreen extends React.Component<Props> {
render() { render() {
return ( return (
<View> <FlatList
<CollapsibleFlatList
data={this.data} data={this.data}
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
renderItem={this.renderItem} renderItem={this.renderItem}
@ -69,7 +67,6 @@ export default class AboutDependenciesScreen extends React.Component<Props> {
removeClippedSubviews={true} removeClippedSubviews={true}
getItemLayout={this.itemLayout} getItemLayout={this.itemLayout}
/> />
</View>
); );
} }
} }

View file

@ -6,7 +6,6 @@ import i18n from "i18n-js";
import {Avatar, Card, List, Title, withTheme} from 'react-native-paper'; import {Avatar, Card, List, Title, withTheme} from 'react-native-paper';
import packageJson from "../../../package.json"; import packageJson from "../../../package.json";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
type ListItem = { type ListItem = {
onPressCallback: () => void, onPressCallback: () => void,
@ -339,7 +338,7 @@ class AboutScreen extends React.Component<Props> {
render() { render() {
return ( return (
<CollapsibleFlatList <FlatList
style={{padding: 5}} style={{padding: 5}}
data={this.dataOrder} data={this.dataOrder}
renderItem={this.getMainCard} renderItem={this.getMainCard}

View file

@ -1,14 +1,13 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {View} from "react-native"; import {FlatList, View} from "react-native";
import AsyncStorageManager from "../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../managers/AsyncStorageManager";
import CustomModal from "../../components/Overrides/CustomModal"; import CustomModal from "../../components/Overrides/CustomModal";
import {Button, List, Subheading, TextInput, Title, withTheme} from 'react-native-paper'; import {Button, List, Subheading, TextInput, Title, withTheme} from 'react-native-paper';
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import {Modalize} from "react-native-modalize"; import {Modalize} from "react-native-modalize";
import type {CustomTheme} from "../../managers/ThemeManager"; import type {CustomTheme} from "../../managers/ThemeManager";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
type PreferenceItem = { type PreferenceItem = {
key: string, key: string,
@ -169,7 +168,7 @@ class DebugScreen extends React.Component<Props, State> {
{this.getModalContent()} {this.getModalContent()}
</CustomModal> </CustomModal>
{/*$FlowFixMe*/} {/*$FlowFixMe*/}
<CollapsibleFlatList <FlatList
data={this.state.currentPreferences} data={this.state.currentPreferences}
extraData={this.state.currentPreferences} extraData={this.state.currentPreferences}
renderItem={this.renderItem} renderItem={this.renderItem}

View file

@ -1,13 +1,16 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {FlatList, Image, Linking, View} from 'react-native'; import {Animated, FlatList, Image, Linking, View} from 'react-native';
import {Card, List, Text, withTheme} from 'react-native-paper'; import {Card, List, Text, withTheme} from 'react-native-paper';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import {Collapsible} from "react-navigation-collapsible";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {withCollapsible} from "../../utils/withCollapsible";
import type {MaterialCommunityIconsGlyphs} from "react-native-vector-icons/MaterialCommunityIcons"; import type {MaterialCommunityIconsGlyphs} from "react-native-vector-icons/MaterialCommunityIcons";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
type Props = { type Props = {
collapsibleStack: Collapsible
}; };
type DatasetItem = { type DatasetItem = {
@ -127,14 +130,22 @@ class AmicaleContactScreen extends React.Component<Props> {
}; };
render() { render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return ( return (
<CollapsibleFlatList <Animated.FlatList
data={[{key: "1"}]} data={[{key: "1"}]}
renderItem={this.getScreen} renderItem={this.getScreen}
hasTab={true} // Animations
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/> />
); );
} }
} }
export default withTheme(AmicaleContactScreen); export default withCollapsible(withTheme(AmicaleContactScreen));

View file

@ -1,11 +1,10 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Image, View} from 'react-native'; import {Image, ScrollView, View} from 'react-native';
import {Card, List, Text, withTheme} from 'react-native-paper'; import {Card, List, Text, withTheme} from 'react-native-paper';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import Autolink from "react-native-autolink"; import Autolink from "react-native-autolink";
import CollapsibleScrollView from "../../../components/Collapsible/CollapsibleScrollView";
type Props = {}; type Props = {};
@ -15,7 +14,7 @@ class ClubAboutScreen extends React.Component<Props> {
render() { render() {
return ( return (
<CollapsibleScrollView style={{padding: 5}}> <ScrollView style={{padding: 5}}>
<View style={{ <View style={{
width: '100%', width: '100%',
height: 100, height: 100,
@ -44,7 +43,7 @@ class ClubAboutScreen extends React.Component<Props> {
/> />
</Card.Content> </Card.Content>
</Card> </Card>
</CollapsibleScrollView> </ScrollView>
); );
} }
} }

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Linking, View} from 'react-native'; import {Linking, ScrollView, View} from 'react-native';
import {Avatar, Button, Card, Chip, Paragraph, withTheme} from 'react-native-paper'; import {Avatar, Button, 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";
@ -12,7 +12,6 @@ import type {category, club} from "./ClubListScreen";
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from "../../../managers/ThemeManager";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import {ERROR_TYPE} from "../../../utils/WebData"; import {ERROR_TYPE} from "../../../utils/WebData";
import CollapsibleScrollView from "../../../components/Collapsible/CollapsibleScrollView";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
@ -185,10 +184,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
} }
if (data != null) { if (data != null) {
return ( return (
<CollapsibleScrollView <ScrollView style={{paddingLeft: 5, paddingRight: 5}}>
style={{paddingLeft: 5, paddingRight: 5}}
hasTab={true}
>
{this.getCategoriesRender(data.category)} {this.getCategoriesRender(data.category)}
{data.logo !== null ? {data.logo !== null ?
<View style={{ <View style={{
@ -218,7 +214,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
</Card.Content> </Card.Content>
: <View/>} : <View/>}
{this.getManagersRender(data.responsibles, data.email)} {this.getManagersRender(data.responsibles, data.email)}
</CollapsibleScrollView> </ScrollView>
); );
} else } else
return null; return null;

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Platform} from "react-native"; import {Animated, Platform} from "react-native";
import {Searchbar} from 'react-native-paper'; import {Searchbar} from 'react-native-paper';
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen"; import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
import i18n from "i18n-js"; import i18n from "i18n-js";
@ -9,9 +9,10 @@ import ClubListItem from "../../../components/Lists/Clubs/ClubListItem";
import {isItemInCategoryFilter, stringMatchQuery} from "../../../utils/Search"; import {isItemInCategoryFilter, stringMatchQuery} from "../../../utils/Search";
import ClubListHeader from "../../../components/Lists/Clubs/ClubListHeader"; import ClubListHeader from "../../../components/Lists/Clubs/ClubListHeader";
import MaterialHeaderButtons, {Item} from "../../../components/Overrides/CustomHeaderButton"; import MaterialHeaderButtons, {Item} from "../../../components/Overrides/CustomHeaderButton";
import {withCollapsible} from "../../../utils/withCollapsible";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from "../../../managers/ThemeManager";
import CollapsibleFlatList from "../../../components/Collapsible/CollapsibleFlatList"; import {Collapsible} from "react-navigation-collapsible";
export type category = { export type category = {
id: number, id: number,
@ -31,6 +32,7 @@ export type club = {
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
theme: CustomTheme, theme: CustomTheme,
collapsibleStack: Collapsible,
} }
type State = { type State = {
@ -109,8 +111,9 @@ class ClubListScreen extends React.Component<Props, State> {
clubList = data[0].clubs; clubList = data[0].clubs;
} }
this.categories = categoryList; this.categories = categoryList;
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return ( return (
<CollapsibleFlatList <Animated.FlatList
data={clubList} data={clubList}
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
renderItem={this.getRenderItem} renderItem={this.getRenderItem}
@ -118,6 +121,10 @@ class ClubListScreen extends React.Component<Props, State> {
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration // Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
removeClippedSubviews={true} removeClippedSubviews={true}
getItemLayout={this.itemLayout} getItemLayout={this.itemLayout}
// Animations
onScroll={onScroll}
contentContainerStyle={{paddingTop: containerPaddingTop}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/> />
) )
}; };
@ -234,4 +241,4 @@ class ClubListScreen extends React.Component<Props, State> {
} }
} }
export default ClubListScreen; export default withCollapsible(ClubListScreen);

View file

@ -2,13 +2,14 @@
import * as React from 'react'; import * as React from 'react';
import {Button, Caption, Card, Headline, Paragraph, withTheme} from 'react-native-paper'; import {Button, Caption, Card, Headline, Paragraph, withTheme} from 'react-native-paper';
import {Collapsible} from "react-navigation-collapsible";
import {withCollapsible} from "../../../utils/withCollapsible";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from "../../../managers/ThemeManager";
import type {Device} from "./EquipmentListScreen"; import type {Device} from "./EquipmentListScreen";
import {View} from "react-native"; import {Animated, View} from "react-native";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {getRelativeDateString} from "../../../utils/EquipmentBooking"; import {getRelativeDateString} from "../../../utils/EquipmentBooking";
import CollapsibleScrollView from "../../../components/Collapsible/CollapsibleScrollView";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
@ -19,6 +20,7 @@ type Props = {
}, },
}, },
theme: CustomTheme, theme: CustomTheme,
collapsibleStack: Collapsible,
} }
@ -42,13 +44,21 @@ class EquipmentConfirmScreen extends React.Component<Props> {
} }
render() { render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
const item = this.item; const item = this.item;
const dates = this.dates; const dates = this.dates;
if (item != null && dates != null) { if (item != null && dates != null) {
const start = new Date(dates[0]); const start = new Date(dates[0]);
const end = new Date(dates[1]); const end = new Date(dates[1]);
return ( return (
<CollapsibleScrollView> <Animated.ScrollView
// Animations
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}>
<Card style={{margin: 5}}> <Card style={{margin: 5}}>
<Card.Content> <Card.Content>
<View style={{flex: 1}}> <View style={{flex: 1}}>
@ -93,7 +103,7 @@ class EquipmentConfirmScreen extends React.Component<Props> {
</Paragraph> </Paragraph>
</Card.Content> </Card.Content>
</Card> </Card>
</CollapsibleScrollView> </Animated.ScrollView>
); );
} else } else
return null; return null;
@ -102,4 +112,4 @@ class EquipmentConfirmScreen extends React.Component<Props> {
} }
export default withTheme(EquipmentConfirmScreen); export default withCollapsible(withTheme(EquipmentConfirmScreen));

View file

@ -1,9 +1,11 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {View} from "react-native"; import {Animated, View} from "react-native";
import {Button, withTheme} from 'react-native-paper'; import {Button, withTheme} from 'react-native-paper';
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen"; import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
import {Collapsible} from "react-navigation-collapsible";
import {withCollapsible} from "../../../utils/withCollapsible";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from "../../../managers/ThemeManager";
import i18n from "i18n-js"; import i18n from "i18n-js";
@ -12,11 +14,11 @@ import EquipmentListItem from "../../../components/Lists/Equipment/EquipmentList
import MascotPopup from "../../../components/Mascot/MascotPopup"; import MascotPopup from "../../../components/Mascot/MascotPopup";
import {MASCOT_STYLE} from "../../../components/Mascot/Mascot"; import {MASCOT_STYLE} from "../../../components/Mascot/Mascot";
import AsyncStorageManager from "../../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../../managers/AsyncStorageManager";
import CollapsibleFlatList from "../../../components/Collapsible/CollapsibleFlatList";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
theme: CustomTheme, theme: CustomTheme,
collapsibleStack: Collapsible,
} }
type State = { type State = {
@ -27,7 +29,7 @@ export type Device = {
id: number, id: number,
name: string, name: string,
caution: number, caution: number,
booked_at: Array<{ begin: string, end: string }>, booked_at: Array<{begin: string, end: string}>,
}; };
export type RentedDevice = { export type RentedDevice = {
@ -131,12 +133,20 @@ class EquipmentListScreen extends React.Component<Props, State> {
if (fetchedData != null) if (fetchedData != null)
this.userRents = fetchedData["locations"]; this.userRents = fetchedData["locations"];
} }
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return ( return (
<CollapsibleFlatList <Animated.FlatList
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
renderItem={this.getRenderItem} renderItem={this.getRenderItem}
ListHeaderComponent={this.getListHeader()} ListHeaderComponent={this.getListHeader()}
data={this.data} data={this.data}
// Animations
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/> />
) )
}; };
@ -193,4 +203,4 @@ class EquipmentListScreen extends React.Component<Props, State> {
} }
} }
export default withTheme(EquipmentListScreen); export default withCollapsible(withTheme(EquipmentListScreen));

View file

@ -2,10 +2,12 @@
import * as React from 'react'; import * as React from 'react';
import {Button, Caption, Card, Headline, Subheading, withTheme} from 'react-native-paper'; import {Button, Caption, Card, Headline, Subheading, withTheme} from 'react-native-paper';
import {Collapsible} from "react-navigation-collapsible";
import {withCollapsible} from "../../../utils/withCollapsible";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from "../../../managers/ThemeManager";
import type {Device} from "./EquipmentListScreen"; import type {Device} from "./EquipmentListScreen";
import {BackHandler, View} from "react-native"; import {Animated, BackHandler, View} from "react-native";
import * as Animatable from "react-native-animatable"; import * as Animatable from "react-native-animatable";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {CalendarList} from "react-native-calendars"; import {CalendarList} from "react-native-calendars";
@ -20,7 +22,6 @@ import {
isEquipmentAvailable isEquipmentAvailable
} from "../../../utils/EquipmentBooking"; } from "../../../utils/EquipmentBooking";
import ConnectionManager from "../../../managers/ConnectionManager"; import ConnectionManager from "../../../managers/ConnectionManager";
import CollapsibleScrollView from "../../../components/Collapsible/CollapsibleScrollView";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
@ -30,6 +31,7 @@ type Props = {
}, },
}, },
theme: CustomTheme, theme: CustomTheme,
collapsibleStack: Collapsible,
} }
type State = { type State = {
@ -267,6 +269,8 @@ class EquipmentRentScreen extends React.Component<Props, State> {
} }
render() { render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
const item = this.item; const item = this.item;
const start = this.getBookStartDate(); const start = this.getBookStartDate();
const end = this.getBookEndDate(); const end = this.getBookEndDate();
@ -276,7 +280,14 @@ class EquipmentRentScreen extends React.Component<Props, State> {
const firstAvailability = getFirstEquipmentAvailability(item); const firstAvailability = getFirstEquipmentAvailability(item);
return ( return (
<View style={{flex: 1}}> <View style={{flex: 1}}>
<CollapsibleScrollView> <Animated.ScrollView
// Animations
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}>
<Card style={{margin: 5}}> <Card style={{margin: 5}}>
<Card.Content> <Card.Content>
<View style={{flex: 1}}> <View style={{flex: 1}}>
@ -385,7 +396,7 @@ class EquipmentRentScreen extends React.Component<Props, State> {
}} }}
style={{marginBottom: 50}} style={{marginBottom: 50}}
/> />
</CollapsibleScrollView> </Animated.ScrollView>
<LoadingConfirmDialog <LoadingConfirmDialog
visible={this.state.dialogVisible} visible={this.state.dialogVisible}
onDismiss={this.onDialogDismiss} onDismiss={this.onDialogDismiss}
@ -438,4 +449,4 @@ class EquipmentRentScreen extends React.Component<Props, State> {
} }
export default withTheme(EquipmentRentScreen); export default withCollapsible(withTheme(EquipmentRentScreen));

View file

@ -1,11 +1,13 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Image, KeyboardAvoidingView, StyleSheet, View} from "react-native"; import {Animated, Dimensions, Image, KeyboardAvoidingView, StyleSheet, View} from "react-native";
import {Button, Card, HelperText, TextInput, withTheme} from 'react-native-paper'; import {Button, Card, HelperText, TextInput, withTheme} from 'react-native-paper';
import ConnectionManager from "../../managers/ConnectionManager"; import ConnectionManager from "../../managers/ConnectionManager";
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import ErrorDialog from "../../components/Dialogs/ErrorDialog"; import ErrorDialog from "../../components/Dialogs/ErrorDialog";
import {withCollapsible} from "../../utils/withCollapsible";
import {Collapsible} from "react-navigation-collapsible";
import type {CustomTheme} from "../../managers/ThemeManager"; import type {CustomTheme} from "../../managers/ThemeManager";
import AsyncStorageManager from "../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../managers/AsyncStorageManager";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
@ -13,11 +15,11 @@ import AvailableWebsites from "../../constants/AvailableWebsites";
import {MASCOT_STYLE} from "../../components/Mascot/Mascot"; import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
import MascotPopup from "../../components/Mascot/MascotPopup"; import MascotPopup from "../../components/Mascot/MascotPopup";
import LinearGradient from "react-native-linear-gradient"; import LinearGradient from "react-native-linear-gradient";
import CollapsibleScrollView from "../../components/Collapsible/CollapsibleScrollView";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
route: { params: { nextScreen: string } }, route: { params: { nextScreen: string } },
collapsibleStack: Collapsible,
theme: CustomTheme theme: CustomTheme
} }
@ -54,6 +56,7 @@ class LoginScreen extends React.Component<Props, State> {
onEmailChange: (value: string) => null; onEmailChange: (value: string) => null;
onPasswordChange: (value: string) => null; onPasswordChange: (value: string) => null;
passwordInputRef: { current: null | TextInput }; passwordInputRef: { current: null | TextInput };
windowHeight: number;
nextScreen: string | null; nextScreen: string | null;
@ -63,6 +66,7 @@ class LoginScreen extends React.Component<Props, State> {
this.onEmailChange = this.onInputChange.bind(this, true); this.onEmailChange = this.onInputChange.bind(this, true);
this.onPasswordChange = this.onInputChange.bind(this, false); this.onPasswordChange = this.onInputChange.bind(this, false);
this.props.navigation.addListener('focus', this.onScreenFocus); this.props.navigation.addListener('focus', this.onScreenFocus);
this.windowHeight = Dimensions.get('window').height;
} }
onScreenFocus = () => { onScreenFocus = () => {
@ -355,6 +359,7 @@ class LoginScreen extends React.Component<Props, State> {
} }
render() { render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return ( return (
<LinearGradient <LinearGradient
style={{ style={{
@ -370,10 +375,16 @@ class LoginScreen extends React.Component<Props, State> {
enabled enabled
keyboardVerticalOffset={100} keyboardVerticalOffset={100}
> >
<CollapsibleScrollView> <Animated.ScrollView
<View style={{height: "100%"}}> onScroll={onScroll}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
style={{flex: 1}}
>
<View style={{height: this.windowHeight - containerPaddingTop}}>
{this.getMainCard()} {this.getMainCard()}
</View> </View>
<MascotPopup <MascotPopup
visible={this.state.mascotDialogVisible} visible={this.state.mascotDialogVisible}
title={i18n.t("screens.login.mascotDialog.title")} title={i18n.t("screens.login.mascotDialog.title")}
@ -394,7 +405,7 @@ class LoginScreen extends React.Component<Props, State> {
onDismiss={this.hideErrorDialog} onDismiss={this.hideErrorDialog}
errorCode={this.state.dialogError} errorCode={this.state.dialogError}
/> />
</CollapsibleScrollView> </Animated.ScrollView>
</KeyboardAvoidingView> </KeyboardAvoidingView>
</LinearGradient> </LinearGradient>
); );
@ -420,4 +431,4 @@ const styles = StyleSheet.create({
} }
}); });
export default withTheme(LoginScreen); export default withCollapsible(withTheme(LoginScreen));

View file

@ -1,12 +1,15 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {FlatList, StyleSheet, View} from "react-native"; import {Animated, FlatList, StyleSheet, View} from "react-native";
import {Avatar, Button, Card, Divider, List, Paragraph, withTheme} from 'react-native-paper'; import {Avatar, Button, Card, Divider, List, Paragraph, withTheme} from 'react-native-paper';
import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen"; import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import LogoutDialog from "../../components/Amicale/LogoutDialog"; import LogoutDialog from "../../components/Amicale/LogoutDialog";
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton"; import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {Collapsible} from "react-navigation-collapsible";
import {withCollapsible} from "../../utils/withCollapsible";
import type {cardList} from "../../components/Lists/CardList/CardList"; import type {cardList} from "../../components/Lists/CardList/CardList";
import CardList from "../../components/Lists/CardList/CardList"; import CardList from "../../components/Lists/CardList/CardList";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
@ -14,11 +17,11 @@ import type {CustomTheme} from "../../managers/ThemeManager";
import AvailableWebsites from "../../constants/AvailableWebsites"; import AvailableWebsites from "../../constants/AvailableWebsites";
import Mascot, {MASCOT_STYLE} from "../../components/Mascot/Mascot"; import Mascot, {MASCOT_STYLE} from "../../components/Mascot/Mascot";
import ServicesManager, {SERVICES_KEY} from "../../managers/ServicesManager"; import ServicesManager, {SERVICES_KEY} from "../../managers/ServicesManager";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
theme: CustomTheme, theme: CustomTheme,
collapsibleStack: Collapsible,
} }
type State = { type State = {
@ -94,11 +97,20 @@ class ProfileScreen extends React.Component<Props, State> {
if (data[0] != null) { if (data[0] != null) {
this.data = data[0]; this.data = data[0];
} }
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return ( return (
<View style={{flex: 1}}> <View style={{flex: 1}}>
<CollapsibleFlatList <Animated.FlatList
renderItem={this.getRenderItem} renderItem={this.getRenderItem}
data={this.flatListData} data={this.flatListData}
// Animations
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/> />
<LogoutDialog <LogoutDialog
{...this.props} {...this.props}
@ -429,4 +441,4 @@ const styles = StyleSheet.create({
}); });
export default withTheme(ProfileScreen); export default withCollapsible(withTheme(ProfileScreen));

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {RefreshControl, View} from "react-native"; import {FlatList, RefreshControl, View} from "react-native";
import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen"; import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
import {getTimeOnlyString, stringToDate} from "../../utils/Planning"; import {getTimeOnlyString, stringToDate} from "../../utils/Planning";
import VoteTease from "../../components/Amicale/Vote/VoteTease"; import VoteTease from "../../components/Amicale/Vote/VoteTease";
@ -15,7 +15,6 @@ import MascotPopup from "../../components/Mascot/MascotPopup";
import AsyncStorageManager from "../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../managers/AsyncStorageManager";
import {Button} from "react-native-paper"; import {Button} from "react-native-paper";
import VoteNotAvailable from "../../components/Amicale/Vote/VoteNotAvailable"; import VoteNotAvailable from "../../components/Amicale/Vote/VoteNotAvailable";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
export type team = { export type team = {
id: number, id: number,
@ -247,7 +246,9 @@ export default class VoteScreen extends React.Component<Props, State> {
this.generateDateObject(); this.generateDateObject();
return ( return (
<CollapsibleFlatList <View>
{/*$FlowFixMe*/}
<FlatList
data={this.mainFlatListData} data={this.mainFlatListData}
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
@ -258,6 +259,7 @@ export default class VoteScreen extends React.Component<Props, State> {
extraData={this.state.hasVoted.toString()} extraData={this.state.hasVoted.toString()}
renderItem={this.mainRenderItem} renderItem={this.mainRenderItem}
/> />
</View>
); );
}; };

View file

@ -0,0 +1,26 @@
// @flow
import * as React from "react";
import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager";
import {withTheme} from "react-native-paper";
type Props = {
navigation: StackNavigationProp,
theme: CustomTheme,
}
type State = {
}
class GameEndScreen extends React.Component<Props, State> {
render() {
return (
null
);
}
}
export default withTheme(GameEndScreen);

View file

@ -4,7 +4,7 @@ import * as React from "react";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from "../../../managers/ThemeManager";
import {Button, Card, Divider, Headline, Paragraph, Text, withTheme} from "react-native-paper"; import {Button, Card, Divider, Headline, Paragraph, Text, withTheme} from "react-native-paper";
import {View} from "react-native"; import {ScrollView, View} from "react-native";
import i18n from "i18n-js"; import i18n from "i18n-js";
import Mascot, {MASCOT_STYLE} from "../../../components/Mascot/Mascot"; import Mascot, {MASCOT_STYLE} from "../../../components/Mascot/Mascot";
import MascotPopup from "../../../components/Mascot/MascotPopup"; import MascotPopup from "../../../components/Mascot/MascotPopup";
@ -17,7 +17,6 @@ import * as Animatable from "react-native-animatable";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons"; import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import LinearGradient from "react-native-linear-gradient"; import LinearGradient from "react-native-linear-gradient";
import SpeechArrow from "../../../components/Mascot/SpeechArrow"; import SpeechArrow from "../../../components/Mascot/SpeechArrow";
import CollapsibleScrollView from "../../../components/Collapsible/CollapsibleScrollView";
type GameStats = { type GameStats = {
score: number, score: number,
@ -418,7 +417,7 @@ class GameStartScreen extends React.Component<Props, State> {
start={{x: 0, y: 0}} start={{x: 0, y: 0}}
end={{x: 0, y: 1}} end={{x: 0, y: 1}}
> >
<CollapsibleScrollView> <ScrollView>
{this.getMainContent()} {this.getMainContent()}
<MascotPopup <MascotPopup
visible={this.state.mascotDialogVisible} visible={this.state.mascotDialogVisible}
@ -435,7 +434,7 @@ class GameStartScreen extends React.Component<Props, State> {
}} }}
emotion={MASCOT_STYLE.COOL} emotion={MASCOT_STYLE.COOL}
/> />
</CollapsibleScrollView> </ScrollView>
</LinearGradient> </LinearGradient>
</View> </View>

View file

@ -5,7 +5,7 @@ import {FlatList} from 'react-native';
import i18n from "i18n-js"; import i18n from "i18n-js";
import DashboardItem from "../../components/Home/EventDashboardItem"; import DashboardItem from "../../components/Home/EventDashboardItem";
import WebSectionList from "../../components/Screens/WebSectionList"; import WebSectionList from "../../components/Screens/WebSectionList";
import {ActivityIndicator, Headline, withTheme} from 'react-native-paper'; import {Headline, withTheme} from 'react-native-paper';
import FeedItem from "../../components/Home/FeedItem"; import FeedItem from "../../components/Home/FeedItem";
import SmallDashboardItem from "../../components/Home/SmallDashboardItem"; import SmallDashboardItem from "../../components/Home/SmallDashboardItem";
import PreviewEventDashboardItem from "../../components/Home/PreviewEventDashboardItem"; import PreviewEventDashboardItem from "../../components/Home/PreviewEventDashboardItem";
@ -16,7 +16,6 @@ import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHead
import AnimatedFAB from "../../components/Animations/AnimatedFAB"; import AnimatedFAB from "../../components/Animations/AnimatedFAB";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../managers/ThemeManager"; import type {CustomTheme} from "../../managers/ThemeManager";
import * as Animatable from "react-native-animatable";
import {View} from "react-native-animatable"; import {View} from "react-native-animatable";
import ConnectionManager from "../../managers/ConnectionManager"; import ConnectionManager from "../../managers/ConnectionManager";
import LogoutDialog from "../../components/Amicale/LogoutDialog"; import LogoutDialog from "../../components/Amicale/LogoutDialog";
@ -24,8 +23,6 @@ import AsyncStorageManager from "../../managers/AsyncStorageManager";
import {MASCOT_STYLE} from "../../components/Mascot/Mascot"; import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
import MascotPopup from "../../components/Mascot/MascotPopup"; import MascotPopup from "../../components/Mascot/MascotPopup";
import DashboardManager from "../../managers/DashboardManager"; import DashboardManager from "../../managers/DashboardManager";
import type {ServiceItem} from "../../managers/ServicesManager";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
// import DATA from "../dashboard_data.json"; // import DATA from "../dashboard_data.json";
@ -64,6 +61,11 @@ export type fullDashboard = {
available_tutorials: number, available_tutorials: number,
} }
type dashboardItem = {
id: string,
content: Array<{ [key: string]: any }>
};
export type event = { export type event = {
id: number, id: number,
title: string, title: string,
@ -76,6 +78,12 @@ export type event = {
url: string, url: string,
} }
type listSection = {
title: string,
data: Array<dashboardItem> | Array<feedItem>,
id: string
};
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
route: { params: any, ... }, route: { params: any, ... },
@ -195,41 +203,84 @@ class HomeScreen extends React.Component<Props, State> {
hideDisconnectDialog = () => this.setState({dialogVisible: false}); hideDisconnectDialog = () => this.setState({dialogVisible: false});
openScanner = () => this.props.navigation.navigate("scanner");
/** /**
* Creates the dataset to be used in the FlatList * Creates the dataset to be used in the FlatList
* *
* @param fetchedData * @param fetchedData
* @param isLoading
* @return {*} * @return {*}
*/ */
createDataset = (fetchedData: rawDashboard | null, isLoading: boolean) => { createDataset = (fetchedData: rawDashboard) => {
// fetchedData = DATA; // fetchedData = DATA;
if (fetchedData != null) { let dashboardData;
if (fetchedData.news_feed != null) if (fetchedData.news_feed != null)
this.currentNewFeed = fetchedData.news_feed.data; this.currentNewFeed = fetchedData.news_feed.data;
if (fetchedData.dashboard != null) if (fetchedData.dashboard != null)
this.currentDashboard = fetchedData.dashboard; this.currentDashboard = fetchedData.dashboard;
}
if (this.currentNewFeed.length > 0) if (fetchedData.dashboard != null)
dashboardData = this.generateDashboardDataset(fetchedData.dashboard);
else
dashboardData = this.generateDashboardDataset(null);
return [ return [
{
title: '',
data: dashboardData,
id: SECTIONS_ID[0]
},
{ {
title: i18n.t("screens.home.feedTitle"), title: i18n.t("screens.home.feedTitle"),
data: this.currentNewFeed, data: this.currentNewFeed,
id: SECTIONS_ID[1] id: SECTIONS_ID[1]
} }
]; ];
else
return [
{
title: isLoading ? i18n.t("screens.home.feedLoading") : i18n.t("screens.home.feedError"),
data: [],
id: SECTIONS_ID[1]
}
];
}; };
/**
* Generates the dataset associated to the dashboard to be displayed in the FlatList as a section
*
* @param dashboardData
* @return {Array<dashboardItem>}
*/
generateDashboardDataset(dashboardData: fullDashboard | null): Array<dashboardItem> {
return [
{id: 'actions', content: []},
{
id: 'top',
content: this.dashboardManager.getCurrentDashboard(),
},
{
id: 'event',
content: dashboardData == null ? [] : dashboardData.today_events
},
];
}
/**
* Gets a dashboard item
*
* @param item The item to display
* @return {*}
*/
getDashboardItem(item: dashboardItem) {
let content = item.content;
if (item.id === 'event')
return this.getDashboardEvent(content);
else if (item.id === 'top')
return this.getDashboardRow(content);
else
return this.getDashboardActions();
}
/**
* Gets a dashboard item with action buttons
*
* @returns {*}
*/
getDashboardActions() {
return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>;
}
/** /**
* Gets the time limit depending on the current day: * Gets the time limit depending on the current day:
* 17:30 for every day of the week except for thursday 11:30 * 17:30 for every day of the week except for thursday 11:30
@ -376,13 +427,22 @@ class HomeScreen extends React.Component<Props, State> {
} }
/** /**
* Gets a dashboard item with action buttons * Gets a dashboard shortcut item
* *
* @param item
* @returns {*} * @returns {*}
*/ */
getDashboardActions() { dashboardRowRenderItem = ({item}: { item: DashboardItem }) => {
return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>; return (
} <SmallDashboardItem
image={item.image}
onPress={item.onPress}
badgeCount={this.currentDashboard != null && item.badgeFunction != null
? item.badgeFunction(this.currentDashboard)
: null}
/>
);
};
/** /**
* Gets a dashboard item with a row of shortcut buttons. * Gets a dashboard item with a row of shortcut buttons.
@ -390,7 +450,7 @@ class HomeScreen extends React.Component<Props, State> {
* @param content * @param content
* @return {*} * @return {*}
*/ */
getDashboardRow(content: Array<ServiceItem>) { getDashboardRow(content: Array<DashboardItem>) {
return ( return (
//$FlowFixMe //$FlowFixMe
<FlatList <FlatList
@ -406,24 +466,6 @@ class HomeScreen extends React.Component<Props, State> {
/>); />);
} }
/**
* Gets a dashboard shortcut item
*
* @param item
* @returns {*}
*/
dashboardRowRenderItem = ({item}: { item: ServiceItem }) => {
return (
<SmallDashboardItem
image={item.image}
onPress={item.onPress}
badgeCount={this.currentDashboard != null && item.badgeFunction != null
? item.badgeFunction(this.currentDashboard)
: null}
/>
);
};
/** /**
* Gets a render item for the given feed object * Gets a render item for the given feed object
* *
@ -449,15 +491,28 @@ class HomeScreen extends React.Component<Props, State> {
* @param section The current section * @param section The current section
* @return {*} * @return {*}
*/ */
getRenderItem = ({item}: { item: feedItem, }) => this.getFeedItem(item); getRenderItem = ({item, section}: {
item: { [key: string]: any },
section: listSection
}) => {
if (section.id === SECTIONS_ID[0]) {
const data: dashboardItem = item;
return this.getDashboardItem(data);
} else {
const data: feedItem = item;
return this.getFeedItem(data);
}
};
openScanner = () => this.props.navigation.navigate("scanner");
onScroll = (event: SyntheticEvent<EventTarget>) => { onScroll = (event: SyntheticEvent<EventTarget>) => {
if (this.fabRef.current != null) if (this.fabRef.current != null)
this.fabRef.current.onScroll(event); this.fabRef.current.onScroll(event);
}; };
renderSectionHeader = (data: { section: { [key: string]: any } }, isLoading: boolean) => { renderSectionHeader = (data: { [key: string]: any }) => {
if (data.section.data.length > 0) if (data.section.title !== "")
return ( return (
<Headline style={{ <Headline style={{
textAlign: "center", textAlign: "center",
@ -468,59 +523,7 @@ class HomeScreen extends React.Component<Props, State> {
</Headline> </Headline>
) )
else else
return ( return null;
<View>
<Headline style={{
textAlign: "center",
marginTop: 50,
marginBottom: 10,
marginLeft: 20,
marginRight: 20,
color: this.props.theme.colors.textDisabled
}}>
{data.section.title}
</Headline>
{isLoading
? <ActivityIndicator
style={{
marginTop: 10
}}
/>
: <MaterialCommunityIcons
name={"access-point-network-off"}
size={100}
color={this.props.theme.colors.textDisabled}
style={{
marginLeft: "auto",
marginRight: "auto",
}}
/>}
</View>
);
}
getListHeader = (fetchedData: rawDashboard) => {
let dashboard = null;
if (fetchedData != null) {
dashboard = fetchedData.dashboard;
}
return (
<Animatable.View
animation={"fadeInDown"}
duration={500}
useNativeDriver={true}
>
{this.getDashboardActions()}
{this.getDashboardRow(this.dashboardManager.getCurrentDashboard())}
{this.getDashboardEvent(
dashboard == null
? []
: dashboard.today_events
)}
</Animatable.View>
);
} }
/** /**
@ -553,7 +556,6 @@ class HomeScreen extends React.Component<Props, State> {
onScroll={this.onScroll} onScroll={this.onScroll}
showError={false} showError={false}
renderSectionHeader={this.renderSectionHeader} renderSectionHeader={this.renderSectionHeader}
renderListHeaderComponent={this.getListHeader}
/> />
</View> </View>
<MascotPopup <MascotPopup

View file

@ -3,9 +3,8 @@
import * as React from 'react'; import * as React from 'react';
import {Avatar, Button, Card, Paragraph, withTheme} from "react-native-paper"; import {Avatar, Button, Card, Paragraph, withTheme} from "react-native-paper";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {Linking} from "react-native"; import {Linking, ScrollView} from "react-native";
import type {CustomTheme} from "../../managers/ThemeManager"; import type {CustomTheme} from "../../managers/ThemeManager";
import CollapsibleScrollView from "../../components/Collapsible/CollapsibleScrollView";
type Props = { type Props = {
theme: CustomTheme theme: CustomTheme
@ -77,7 +76,7 @@ class FeedbackScreen extends React.Component<Props> {
render() { render() {
return ( return (
<CollapsibleScrollView style={{padding: 5}}> <ScrollView style={{padding: 5}}>
<Card> <Card>
<Card.Title <Card.Title
title={i18n.t('screens.feedback.bugs')} title={i18n.t('screens.feedback.bugs')}
@ -108,7 +107,7 @@ class FeedbackScreen extends React.Component<Props> {
</Card.Content> </Card.Content>
{this.getButtons(false)} {this.getButtons(false)}
</Card> </Card>
</CollapsibleScrollView> </ScrollView>
); );
} }
} }

View file

@ -13,7 +13,6 @@ import DashboardEditAccordion from "../../../components/Lists/DashboardEdit/Dash
import DashboardEditPreviewItem from "../../../components/Lists/DashboardEdit/DashboardEditPreviewItem"; import DashboardEditPreviewItem from "../../../components/Lists/DashboardEdit/DashboardEditPreviewItem";
import AsyncStorageManager from "../../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../../managers/AsyncStorageManager";
import i18n from "i18n-js"; import i18n from "i18n-js";
import CollapsibleFlatList from "../../../components/Collapsible/CollapsibleFlatList";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
@ -109,43 +108,43 @@ class DashboardEditScreen extends React.Component<Props, State> {
); );
} }
getListHeader() {
render() {
return ( return (
<Card style={{margin: 5}}> <View style={{flex: 1}}>
<Card.Content> <View style={{
<View style={{padding: 5}}> padding: 5
}}>
<Button <Button
mode={"contained"} mode={"contained"}
onPress={this.undoDashboard} onPress={this.undoDashboard}
style={{ style={{
marginLeft: "auto", marginLeft: "auto",
marginRight: "auto", marginRight: "auto",
marginBottom: 10,
}} }}
> >
{i18n.t("screens.settings.dashboardEdit.undo")} {i18n.t("screens.settings.dashboardEdit.undo")}
</Button> </Button>
<View style={{height: 50}}> <View style={{
height: 50
}}>
{this.getDashboard(this.state.currentDashboard)} {this.getDashboard(this.state.currentDashboard)}
</View> </View>
</View> </View>
<Paragraph style={{textAlign: "center"}}> <FlatList
{i18n.t("screens.settings.dashboardEdit.message")}
</Paragraph>
</Card.Content>
</Card>
);
}
render() {
return (
<CollapsibleFlatList
data={this.content} data={this.content}
renderItem={this.renderItem} renderItem={this.renderItem}
ListHeaderComponent={this.getListHeader()} ListHeaderComponent={<Card style={{margin: 5}}>
<Card.Content>
<Paragraph
style={{textAlign: "center"}}>{i18n.t("screens.settings.dashboardEdit.message")}</Paragraph>
</Card.Content>
</Card>}
style={{}} style={{}}
/> />
</View>
); );
} }

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {View} from "react-native"; import {ScrollView, View} from "react-native";
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from "../../../managers/ThemeManager";
import ThemeManager from '../../../managers/ThemeManager'; import ThemeManager from '../../../managers/ThemeManager';
import i18n from "i18n-js"; import i18n from "i18n-js";
@ -10,7 +10,6 @@ import {Card, List, Switch, ToggleButton, withTheme} from 'react-native-paper';
import {Appearance} from "react-native-appearance"; import {Appearance} from "react-native-appearance";
import CustomSlider from "../../../components/Overrides/CustomSlider"; import CustomSlider from "../../../components/Overrides/CustomSlider";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import CollapsibleScrollView from "../../../components/Collapsible/CollapsibleScrollView";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
@ -188,7 +187,7 @@ class SettingsScreen extends React.Component<Props, State> {
render() { render() {
return ( return (
<CollapsibleScrollView> <ScrollView>
<Card style={{margin: 5}}> <Card style={{margin: 5}}>
<Card.Title title={i18n.t('screens.settings.generalCard')}/> <Card.Title title={i18n.t('screens.settings.generalCard')}/>
<List.Section> <List.Section>
@ -265,7 +264,7 @@ class SettingsScreen extends React.Component<Props, State> {
)} )}
</List.Section> </List.Section>
</Card> </Card>
</CollapsibleScrollView> </ScrollView>
); );
} }
} }

View file

@ -1,12 +1,11 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Image, View} from 'react-native'; import {Image, ScrollView, View} from 'react-native';
import i18n from "i18n-js"; import i18n from "i18n-js";
import {Card, List, Paragraph, Text} from 'react-native-paper'; import {Card, List, Paragraph, Text} from 'react-native-paper';
import CustomTabBar from "../../../components/Tabbar/CustomTabBar"; import CustomTabBar from "../../../components/Tabbar/CustomTabBar";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import CollapsibleScrollView from "../../../components/Collapsible/CollapsibleScrollView";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
@ -21,7 +20,7 @@ export default class ProximoAboutScreen extends React.Component<Props> {
render() { render() {
return ( return (
<CollapsibleScrollView style={{padding: 5}}> <ScrollView style={{padding: 5}}>
<View style={{ <View style={{
width: '100%', width: '100%',
height: 100, height: 100,
@ -53,7 +52,7 @@ export default class ProximoAboutScreen extends React.Component<Props> {
<Paragraph>{i18n.t('screens.proximo.paymentMethodsDescription')}</Paragraph> <Paragraph>{i18n.t('screens.proximo.paymentMethodsDescription')}</Paragraph>
</Card.Content> </Card.Content>
</Card> </Card>
</CollapsibleScrollView> </ScrollView>
); );
} }
} }

View file

@ -1,16 +1,17 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Image, Platform, ScrollView, View} from "react-native"; import {Animated, Image, Platform, ScrollView, View} from "react-native";
import i18n from "i18n-js"; import i18n from "i18n-js";
import CustomModal from "../../../components/Overrides/CustomModal"; import CustomModal from "../../../components/Overrides/CustomModal";
import {RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper"; import {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/Proximo/ProximoListItem"; import ProximoListItem from "../../../components/Lists/Proximo/ProximoListItem";
import MaterialHeaderButtons, {Item} from "../../../components/Overrides/CustomHeaderButton"; import MaterialHeaderButtons, {Item} from "../../../components/Overrides/CustomHeaderButton";
import {withCollapsible} from "../../../utils/withCollapsible";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from "../../../managers/ThemeManager";
import CollapsibleFlatList from "../../../components/Collapsible/CollapsibleFlatList"; import {Collapsible} from "react-navigation-collapsible";
function sortPrice(a, b) { function sortPrice(a, b) {
return a.price - b.price; return a.price - b.price;
@ -42,6 +43,7 @@ type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
route: { params: { data: { data: Object }, shouldFocusSearchBar: boolean } }, route: { params: { data: { data: Object }, shouldFocusSearchBar: boolean } },
theme: CustomTheme, theme: CustomTheme,
collapsibleStack: Collapsible,
} }
type State = { type State = {
@ -298,6 +300,7 @@ class ProximoListScreen extends React.Component<Props, State> {
itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index}); itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
render() { render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return ( return (
<View style={{ <View style={{
height: '100%' height: '100%'
@ -305,7 +308,7 @@ class ProximoListScreen extends React.Component<Props, State> {
<CustomModal onRef={this.onModalRef}> <CustomModal onRef={this.onModalRef}>
{this.state.modalCurrentDisplayItem} {this.state.modalCurrentDisplayItem}
</CustomModal> </CustomModal>
<CollapsibleFlatList <Animated.FlatList
data={this.listData} data={this.listData}
extraData={this.state.currentSearchString + this.state.currentSortMode} extraData={this.state.currentSearchString + this.state.currentSortMode}
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
@ -314,10 +317,16 @@ class ProximoListScreen extends React.Component<Props, State> {
removeClippedSubviews={true} removeClippedSubviews={true}
getItemLayout={this.itemLayout} getItemLayout={this.itemLayout}
initialNumToRender={10} initialNumToRender={10}
// Animations
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/> />
</View> </View>
); );
} }
} }
export default withTheme(ProximoListScreen); export default withCollapsible(withTheme(ProximoListScreen));

View file

@ -3,20 +3,24 @@
import * as React from 'react'; import * as React from 'react';
import type {cardList} from "../../components/Lists/CardList/CardList"; import type {cardList} from "../../components/Lists/CardList/CardList";
import CardList from "../../components/Lists/CardList/CardList"; import CardList from "../../components/Lists/CardList/CardList";
import {Image, View} from "react-native"; import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {withCollapsible} from "../../utils/withCollapsible";
import {Collapsible} from "react-navigation-collapsible";
import {Animated, Image, View} from "react-native";
import {Avatar, Card, Divider, List, TouchableRipple, withTheme} from "react-native-paper"; import {Avatar, Card, Divider, List, TouchableRipple, withTheme} from "react-native-paper";
import type {CustomTheme} from "../../managers/ThemeManager"; import type {CustomTheme} from "../../managers/ThemeManager";
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton"; import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
import ConnectionManager from "../../managers/ConnectionManager";
import {StackNavigationProp} from "@react-navigation/stack"; import {StackNavigationProp} from "@react-navigation/stack";
import {MASCOT_STYLE} from "../../components/Mascot/Mascot"; import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
import MascotPopup from "../../components/Mascot/MascotPopup"; import MascotPopup from "../../components/Mascot/MascotPopup";
import AsyncStorageManager from "../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../managers/AsyncStorageManager";
import ServicesManager, {SERVICES_CATEGORIES_KEY} from "../../managers/ServicesManager"; import ServicesManager, {SERVICES_CATEGORIES_KEY} from "../../managers/ServicesManager";
import CollapsibleFlatList from "../../components/Collapsible/CollapsibleFlatList";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: StackNavigationProp,
collapsibleStack: Collapsible,
theme: CustomTheme, theme: CustomTheme,
} }
@ -102,6 +106,18 @@ class ServicesScreen extends React.Component<Props, State> {
/> />
} }
/**
* Redirects to the given route or to the login screen if user is not logged in.
*
* @param route The route to navigate to
*/
onAmicaleServicePress(route: string) {
if (ConnectionManager.getInstance().isLoggedIn())
this.props.navigation.navigate(route);
else
this.props.navigation.navigate("login", {nextScreen: route});
}
/** /**
* A list item showing a list of available services for the current category * A list item showing a list of available services for the current category
* *
@ -139,14 +155,20 @@ class ServicesScreen extends React.Component<Props, State> {
} }
render() { render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return ( return (
<View> <View>
<CollapsibleFlatList <Animated.FlatList
data={this.finalDataset} data={this.finalDataset}
renderItem={this.renderItem} renderItem={this.renderItem}
keyExtractor={this.keyExtractor} keyExtractor={this.keyExtractor}
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
ItemSeparatorComponent={() => <Divider/>} ItemSeparatorComponent={() => <Divider/>}
hasTab={true}
/> />
<MascotPopup <MascotPopup
visible={this.state.mascotDialogVisible} visible={this.state.mascotDialogVisible}
@ -168,4 +190,4 @@ class ServicesScreen extends React.Component<Props, State> {
} }
} }
export default withTheme(ServicesScreen); export default withCollapsible(withTheme(ServicesScreen));

View file

@ -24,7 +24,7 @@ import StackNavigator, {StackNavigationOptions} from "@react-navigation/stack";
export function createScreenCollapsibleStack( export function createScreenCollapsibleStack(
name: string, name: string,
Stack: StackNavigator, Stack: StackNavigator,
component: React.ComponentType<any>, component: React.Node,
title: string, title: string,
useNativeDriver?: boolean, useNativeDriver?: boolean,
options?: StackNavigationOptions, options?: StackNavigationOptions,

View file

@ -12,12 +12,30 @@ import {useCollapsibleStack} from "react-navigation-collapsible";
* This component will then receive the collapsibleStack prop. * This component will then receive the collapsibleStack prop.
* *
* @param Component The component to use Collapsible with * @param Component The component to use Collapsible with
* @returns {React.ComponentType<any>} * @returns {React.ComponentType<React.ClassAttributes<unknown>>}
*/ */
export const withCollapsible = (Component: React.ComponentType<any>) => { export const withCollapsible = (Component: any) => {
return React.forwardRef((props: any, ref: any) => { return React.forwardRef((props: any, ref: any) => {
const {
onScroll,
onScrollWithListener,
containerPaddingTop,
scrollIndicatorInsetTop,
translateY,
progress,
opacity,
} = useCollapsibleStack();
return <Component return <Component
collapsibleStack={useCollapsibleStack()} collapsibleStack={{
onScroll,
onScrollWithListener,
containerPaddingTop: containerPaddingTop,
scrollIndicatorInsetTop: scrollIndicatorInsetTop,
translateY,
progress,
opacity,
}}
ref={ref} ref={ref}
{...props} {...props}
/>; />;