Compare commits

..

No commits in common. "91853092becf8a20edf5ba232d9130911bd1a421" and "41eaaac350e09c1c73b0b227a3ba04ea81e722c2" have entirely different histories.

15 changed files with 156 additions and 229 deletions

View file

@ -3,9 +3,8 @@
import * as React from 'react';
import {StyleSheet, View} from "react-native";
import {FAB, IconButton, Surface, withTheme} from "react-native-paper";
import AutoHideHandler from "../../utils/AutoHideHandler";
import AutoHideComponent from "./AutoHideComponent";
import * as Animatable from 'react-native-animatable';
import CustomTabBar from "../Tabbar/CustomTabBar";
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
@ -29,7 +28,6 @@ const DISPLAY_MODES = {
class AnimatedBottomBar extends React.Component<Props, State> {
ref: Object;
hideHandler: AutoHideHandler;
displayModeIcons: Object;
@ -40,9 +38,6 @@ class AnimatedBottomBar extends React.Component<Props, State> {
constructor() {
super();
this.ref = React.createRef();
this.hideHandler = new AutoHideHandler(false);
this.hideHandler.addListener(this.onHideChange);
this.displayModeIcons = {};
this.displayModeIcons[DISPLAY_MODES.DAY] = "calendar-text";
this.displayModeIcons[DISPLAY_MODES.WEEK] = "calendar-week";
@ -54,17 +49,8 @@ class AnimatedBottomBar extends React.Component<Props, State> {
|| (nextState.currentMode !== this.state.currentMode);
}
onHideChange = (shouldHide: boolean) => {
if (this.ref.current) {
if (shouldHide)
this.ref.current.fadeOutDown(600);
else
this.ref.current.fadeInUp(500);
}
}
onScroll = (event: Object) => {
this.hideHandler.onScroll(event);
this.ref.current.onScroll(event);
};
changeDisplayMode = () => {
@ -88,13 +74,9 @@ class AnimatedBottomBar extends React.Component<Props, State> {
render() {
const buttonColor = this.props.theme.colors.primary;
return (
<Animatable.View
<AutoHideComponent
ref={this.ref}
useNativeDriver
style={{
...styles.container,
bottom: 10 + CustomTabBar.TAB_BAR_HEIGHT
}}>
style={styles.container}>
<Surface style={styles.surface}>
<View style={styles.fabContainer}>
<AnimatedFAB
@ -132,7 +114,7 @@ class AnimatedBottomBar extends React.Component<Props, State> {
onPress={() => this.props.onPress('next', undefined)}/>
</View>
</Surface>
</Animatable.View>
</AutoHideComponent>
);
}
}
@ -141,6 +123,7 @@ const styles = StyleSheet.create({
container: {
position: 'absolute',
left: '5%',
bottom: 10,
width: '90%',
},
surface: {

View file

@ -3,56 +3,42 @@
import * as React from 'react';
import {StyleSheet} from "react-native";
import {FAB} from "react-native-paper";
import AutoHideHandler from "../../utils/AutoHideHandler";
import * as Animatable from 'react-native-animatable';
import CustomTabBar from "../Tabbar/CustomTabBar";
import {AnimatedValue} from "react-native-reanimated";
import AutoHideComponent from "./AutoHideComponent";
type Props = {
navigation: Object,
icon: string,
onPress: Function,
}
const AnimatedFab = Animatable.createAnimatableComponent(FAB);
type State = {
fabPosition: AnimatedValue
}
export default class AnimatedFAB extends React.Component<Props> {
export default class AnimatedFAB extends React.Component<Props, State> {
ref: Object;
hideHandler: AutoHideHandler;
constructor() {
super();
this.ref = React.createRef();
this.hideHandler = new AutoHideHandler(false);
this.hideHandler.addListener(this.onHideChange);
}
onScroll = (event: Object) => {
this.hideHandler.onScroll(event);
this.ref.current.onScroll(event);
};
onHideChange = (shouldHide: boolean) => {
if (this.ref.current) {
if (shouldHide)
this.ref.current.bounceOutDown(1000);
else
this.ref.current.bounceInUp(1000);
}
}
render() {
return (
<AnimatedFab
<AutoHideComponent
ref={this.ref}
useNativeDriver
icon={this.props.icon}
onPress={this.props.onPress}
style={{
...styles.fab,
bottom: CustomTabBar.TAB_BAR_HEIGHT
}}
/>
);
style={styles.fab}>
<FAB
icon={this.props.icon}
onPress={this.props.onPress}
/>
</AutoHideComponent>
);
}
}
@ -61,5 +47,6 @@ const styles = StyleSheet.create({
position: 'absolute',
margin: 16,
right: 0,
bottom: 0,
},
});

View file

@ -0,0 +1,78 @@
// @flow
import * as React from 'react';
import {Animated} from 'react-native'
import {AnimatedValue} from "react-native-reanimated";
type Props = {
children: React.Node,
style: Object,
}
type State = {
fabPosition: AnimatedValue
}
export default class AutoHideComponent extends React.Component<Props, State> {
isAnimationDownPlaying: boolean;
isAnimationUpPlaying: boolean;
downAnimation;
upAnimation;
lastOffset: number;
state = {
fabPosition: new Animated.Value(0),
};
constructor() {
super();
}
onScroll({nativeEvent}: Object) {
const speed = nativeEvent.contentOffset.y < 0 ? 0 : this.lastOffset - nativeEvent.contentOffset.y;
if (speed < -5) { // Go down
if (!this.isAnimationDownPlaying) {
this.isAnimationDownPlaying = true;
if (this.isAnimationUpPlaying)
this.upAnimation.stop();
this.downAnimation = Animated.spring(this.state.fabPosition, {
toValue: 100,
duration: 50,
useNativeDriver: true,
});
this.downAnimation.start(() => {
this.isAnimationDownPlaying = false
});
}
} else if (speed > 5) { // Go up
if (!this.isAnimationUpPlaying) {
this.isAnimationUpPlaying = true;
if (this.isAnimationDownPlaying)
this.downAnimation.stop();
this.upAnimation = Animated.spring(this.state.fabPosition, {
toValue: 0,
duration: 50,
useNativeDriver: true,
});
this.upAnimation.start(() => {
this.isAnimationUpPlaying = false
});
}
}
this.lastOffset = nativeEvent.contentOffset.y;
}
render() {
return (
<Animated.View style={{
...this.props.style,
transform: [{translateY: this.state.fabPosition}]
}}>
{this.props.children}
</Animated.View>
);
}
}

View file

@ -4,13 +4,11 @@ import * as React from 'react';
import {ERROR_TYPE, readData} from "../../utils/WebData";
import i18n from "i18n-js";
import {Snackbar} from 'react-native-paper';
import {Animated, RefreshControl, View} from "react-native";
import {Animated, Platform, RefreshControl, StatusBar, View} from "react-native";
import ErrorView from "../Custom/ErrorView";
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
import {withCollapsible} from "../../utils/withCollapsible";
import * as Animatable from 'react-native-animatable';
import AutoHideHandler from "../../utils/AutoHideHandler";
import CustomTabBar from "../Tabbar/CustomTabBar";
type Props = {
navigation: Object,
@ -54,7 +52,6 @@ class WebSectionList extends React.PureComponent<Props, State> {
refreshInterval: IntervalID;
lastRefresh: Date;
hideHandler: AutoHideHandler;
state = {
refreshing: false,
@ -75,8 +72,6 @@ class WebSectionList extends React.PureComponent<Props, State> {
this.onFetchSuccess = this.onFetchSuccess.bind(this);
this.onFetchError = this.onFetchError.bind(this);
this.getEmptySectionHeader = this.getEmptySectionHeader.bind(this);
this.hideHandler = new AutoHideHandler(false);
this.hideHandler.addListener(this.onHideChange);
}
/**
@ -204,22 +199,18 @@ class WebSectionList extends React.PureComponent<Props, State> {
);
}
onScroll = (event: Object) => {
this.hideHandler.onScroll(event);
if (this.props.onScroll)
this.props.onScroll(event);
}
onHideChange = (shouldHide: boolean) => {
this.props.navigation.setParams({hideTabBar: shouldHide});
}
render() {
let dataset = [];
if (this.state.fetchedData !== undefined)
dataset = this.props.createDataset(this.state.fetchedData);
const shouldRenderHeader = this.props.renderSectionHeader !== null;
const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
const inset = Platform.OS === 'android'
? scrollIndicatorInsetTop - StatusBar.currentHeight
: scrollIndicatorInsetTop;
return (
<View>
{/*$FlowFixMe*/}
@ -228,7 +219,7 @@ class WebSectionList extends React.PureComponent<Props, State> {
extraData={this.props.updateData}
refreshControl={
<RefreshControl
progressViewOffset={containerPaddingTop}
progressViewOffset={padding}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh}
/>
@ -248,13 +239,12 @@ class WebSectionList extends React.PureComponent<Props, State> {
}
getItemLayout={this.props.itemHeight !== null ? this.itemLayout : undefined}
// Animations
onScroll={onScrollWithListener(this.onScroll)}
onScroll={onScrollWithListener(this.props.onScroll)}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
paddingTop: padding,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
scrollIndicatorInsets={{top: inset}}
/>
<Snackbar
visible={this.state.snackbarVisible}

View file

@ -9,9 +9,8 @@ import MaterialHeaderButtons, {Item} from '../Custom/HeaderButton';
import {HiddenItem} from "react-navigation-header-buttons";
import {Linking} from "expo";
import i18n from 'i18n-js';
import {Animated, BackHandler} from "react-native";
import {Animated, BackHandler, Platform, StatusBar} from "react-native";
import {withCollapsible} from "../../utils/withCollapsible";
import AutoHideHandler from "../../utils/AutoHideHandler";
type Props = {
navigation: Object,
@ -34,7 +33,6 @@ class WebViewScreen extends React.PureComponent<Props> {
};
webviewRef: Object;
hideHandler: AutoHideHandler;
canGoBack: boolean;
@ -42,8 +40,6 @@ class WebViewScreen extends React.PureComponent<Props> {
super();
this.webviewRef = React.createRef();
this.canGoBack = false;
this.hideHandler = new AutoHideHandler(false);
this.hideHandler.addListener(this.onHideChange);
}
/**
@ -135,18 +131,11 @@ class WebViewScreen extends React.PureComponent<Props> {
);
}
onScroll = (event: Object) => {
this.hideHandler.onScroll(event);
if (this.props.onScroll)
this.props.onScroll(event);
}
onHideChange = (shouldHide: boolean) => {
this.props.navigation.setParams({hideTabBar: shouldHide});
}
render() {
const {containerPaddingTop, onScrollWithListener} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
return (
<AnimatedWebView
ref={this.webviewRef}
@ -163,9 +152,9 @@ class WebViewScreen extends React.PureComponent<Props> {
this.canGoBack = navState.canGoBack;
}}
onMessage={this.props.onMessage}
onLoad={() => this.injectJavaScript(this.getJavascriptPadding(containerPaddingTop))}
onLoad={() => this.injectJavaScript(this.getJavascriptPadding(padding))}
// Animations
onScroll={onScrollWithListener(this.onScroll)}
onScroll={onScrollWithListener(this.props.onScroll)}
/>
);
}

View file

@ -11,24 +11,16 @@ type Props = {
theme: Object,
}
const TAB_BAR_HEIGHT = 48;
/**
* Abstraction layer for Agenda component, using custom configuration
*/
class CustomTabBar extends React.Component<Props> {
static TAB_BAR_HEIGHT = 48;
// shouldComponentUpdate(nextProps: Props): boolean {
// return (nextProps.theme.dark !== this.props.theme.dark)
// || (nextProps.state.index !== this.props.state.index);
// }
isHidden: boolean;
tabRef: Object;
constructor() {
super();
this.tabRef = React.createRef();
shouldComponentUpdate(nextProps: Props): boolean {
return (nextProps.theme.dark !== this.props.theme.dark)
|| (nextProps.state.index !== this.props.state.index);
}
onItemPress(route: Object, currentIndex: number, destIndex: number) {
@ -51,18 +43,12 @@ class CustomTabBar extends React.Component<Props> {
const navigation = this.props.navigation;
return (
<Animatable.View
ref={this.tabRef}
animation={"fadeInUp"}
duration={500}
useNativeDriver
style={{
flexDirection: 'row',
height: CustomTabBar.TAB_BAR_HEIGHT,
width: '100%',
position: 'absolute',
bottom: 0,
left: 0,
backgroundColor: this.props.theme.colors.surface,
height: TAB_BAR_HEIGHT,
}}
>
{state.routes.map((route, index) => {
@ -84,20 +70,6 @@ class CustomTabBar extends React.Component<Props> {
target: route.key,
});
};
if (isFocused) {
const tabVisible = options.tabBarVisible();
console.log(tabVisible);
if (this.tabRef.current) {
if (this.isHidden && tabVisible) {
this.isHidden = false;
this.tabRef.current.slideInUp(300);
} else if (!this.isHidden && !tabVisible){
this.isHidden = true;
this.tabRef.current.slideOutDown(300);
}
}
}
const color = isFocused ? options.activeColor : options.inactiveColor;
const iconData = {focused: isFocused, color: color};

View file

@ -343,18 +343,9 @@ class TabNavigator extends React.Component<Props> {
else
return null;
},
tabBarVisible: () => {
const state = route.state;
// Get the current route in the stack
const screen = state ? state.routes[state.index] : undefined;
const params = screen ? screen.params : undefined;
const hideTabBar = params ? params.hideTabBar : undefined;
return hideTabBar !== undefined ? !hideTabBar : true;
},
animationEnabled: true,
tabBarLabel: route.name !== 'home' ? undefined : '',
activeColor: this.props.theme.colors.primary,
inactiveColor: this.props.theme.colors.tabIcon,
inactiveColor: this.props.theme.colors.tabIcon
})}
tabBar={props => <CustomTabBar {...props} />}
>

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import {Animated, Platform} from "react-native";
import {Animated, Platform, StatusBar} from "react-native";
import {Chip, Searchbar, withTheme} from 'react-native-paper';
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
import i18n from "i18n-js";
@ -97,6 +97,12 @@ class ClubListScreen extends React.Component<Props, State> {
getScreen = (data: Object) => {
this.categories = data[0].categories;
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
const inset = Platform.OS === 'android'
? scrollIndicatorInsetTop - StatusBar.currentHeight
: scrollIndicatorInsetTop;
return (
//$FlowFixMe
<Animated.FlatList
@ -109,8 +115,8 @@ class ClubListScreen extends React.Component<Props, State> {
getItemLayout={this.itemLayout}
// Animations
onScroll={onScroll}
contentContainerStyle={{paddingTop: containerPaddingTop}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
contentContainerStyle={{paddingTop: padding}}
scrollIndicatorInsets={{top: inset}}
/>
)
};

View file

@ -15,6 +15,7 @@ type Props = {
navigation: Object,
route: Object,
theme: Object,
collapsibleStack: Object,
}
type State = {

View file

@ -485,7 +485,6 @@ class HomeScreen extends React.Component<Props, State> {
onScroll={this.onScroll}
/>
<AnimatedFAB
{...this.props}
ref={this.fabRef}
icon="qrcode-scan"
onPress={this.openScanner}

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import {Animated, Image, Platform, ScrollView, View} from "react-native";
import {Animated, Image, Platform, ScrollView, StatusBar, View} from "react-native";
import i18n from "i18n-js";
import CustomModal from "../../components/Custom/CustomModal";
import {RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper";
@ -9,8 +9,6 @@ import {stringMatchQuery} from "../../utils/Search";
import ProximoListItem from "../../components/Lists/ProximoListItem";
import MaterialHeaderButtons, {Item} from "../../components/Custom/HeaderButton";
import {withCollapsible} from "../../utils/withCollapsible";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import AutoHideHandler from "../../utils/AutoHideHandler";
function sortPrice(a, b) {
return a.price - b.price;
@ -59,7 +57,6 @@ class ProximoListScreen extends React.Component<Props, State> {
modalRef: Object;
listData: Array<Object>;
shouldFocusSearchBar: boolean;
hideHandler: AutoHideHandler;
constructor(props) {
super(props);
@ -70,8 +67,6 @@ class ProximoListScreen extends React.Component<Props, State> {
currentSortMode: 3,
modalCurrentDisplayItem: null,
};
this.hideHandler = new AutoHideHandler(false);
this.hideHandler.addListener(this.onHideChange);
}
@ -301,17 +296,14 @@ class ProximoListScreen extends React.Component<Props, State> {
itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
onScroll = (event: Object) => {
this.hideHandler.onScroll(event);
};
onHideChange = (shouldHide: boolean) => {
this.props.navigation.setParams({hideTabBar: shouldHide});
}
render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack;
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
const inset = Platform.OS === 'android'
? scrollIndicatorInsetTop - StatusBar.currentHeight
: scrollIndicatorInsetTop;
return (
<View style={{
height: '100%'
@ -330,12 +322,9 @@ class ProximoListScreen extends React.Component<Props, State> {
getItemLayout={this.itemLayout}
initialNumToRender={10}
// Animations
onScroll={onScrollWithListener(this.onScroll)}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
onScroll={onScroll}
contentContainerStyle={{paddingTop: padding}}
scrollIndicatorInsets={{top: inset}}
/>
</View>
);

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import {Alert, Platform, View} from 'react-native';
import {Alert, Platform, StatusBar, View} from 'react-native';
import i18n from "i18n-js";
import WebSectionList from "../../components/Lists/WebSectionList";
import * as Notifications from "../../utils/Notifications";
@ -421,13 +421,16 @@ class ProxiwashScreen extends React.Component<Props, State> {
render() {
const nav = this.props.navigation;
const {containerPaddingTop} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
return (
<AnimatedFocusView
{...this.props}
>
<Banner
style={{
marginTop: this.state.bannerVisible ? containerPaddingTop : 0,
marginTop: this.state.bannerVisible ? padding : 0,
}}
visible={this.state.bannerVisible}
actions={[

View file

@ -5,7 +5,7 @@ import ThemeManager from "../../managers/ThemeManager";
import WebViewScreen from "../../components/Screens/WebViewScreen";
import {Avatar, Banner, withTheme} from "react-native-paper";
import i18n from "i18n-js";
import {View} from "react-native";
import {Platform, StatusBar, View} from "react-native";
import AsyncStorageManager from "../../managers/AsyncStorageManager";
import AlertDialog from "../../components/Dialog/AlertDialog";
import {withCollapsible} from "../../utils/withCollapsible";
@ -299,13 +299,16 @@ class PlanexScreen extends React.Component<Props, State> {
render() {
const {containerPaddingTop} = this.props.collapsibleStack;
const padding = Platform.OS === 'android' // Fix for android non translucent bar on expo
? containerPaddingTop - StatusBar.currentHeight
: containerPaddingTop;
return (
<AnimatedFocusView
{...this.props}
>
<Banner
style={{
marginTop: this.state.bannerVisible ? containerPaddingTop : 0,
marginTop: this.state.bannerVisible ? padding : 0,
}}
visible={this.state.bannerVisible}
actions={[

View file

@ -1,41 +0,0 @@
// @flow
import * as React from 'react';
const speedOffset = 5;
export default class AutoHideHandler {
lastOffset: number;
isHidden: boolean;
listeners: Array<Function>;
constructor(startHidden: boolean) {
this.listeners = [];
this.isHidden = startHidden;
}
addListener(listener: Function) {
this.listeners.push(listener);
}
notifyListeners(shouldHide: boolean) {
for (let i = 0; i < this.listeners.length; i++) {
this.listeners[i](shouldHide);
}
}
onScroll({nativeEvent}: Object) {
const speed = nativeEvent.contentOffset.y < 0 ? 0 : this.lastOffset - nativeEvent.contentOffset.y;
if (speed < -speedOffset && !this.isHidden) { // Go down
this.notifyListeners(true);
this.isHidden = true;
} else if (speed > speedOffset && this.isHidden) { // Go up
this.notifyListeners(false);
this.isHidden = false;
}
this.lastOffset = nativeEvent.contentOffset.y;
}
}

View file

@ -1,31 +1,8 @@
import React from 'react';
import {StatusBar} from 'react-native';
import {useCollapsibleStack} from "react-navigation-collapsible";
export const withCollapsible = (Component: any) => {
return React.forwardRef((props: any, ref: any) => {
const {
onScroll,
onScrollWithListener,
containerPaddingTop,
scrollIndicatorInsetTop,
translateY,
progress,
opacity,
} = useCollapsibleStack();
return <Component
collapsibleStack={{
onScroll,
onScrollWithListener,
containerPaddingTop: containerPaddingTop - StatusBar.currentHeight,
scrollIndicatorInsetTop: scrollIndicatorInsetTop - StatusBar.currentHeight,
translateY,
progress,
opacity,
}}
ref={ref}
{...props}
/>;
return <Component collapsibleStack={useCollapsibleStack()} ref={ref} {...props} />;
});
};