Compare commits
12 commits
54861d729d
...
26cf3cf2c3
| Author | SHA1 | Date | |
|---|---|---|---|
| 26cf3cf2c3 | |||
| 96394972eb | |||
| b336c95f20 | |||
| ef23280493 | |||
| 7998fa47ca | |||
| 46d293564f | |||
| 5a9f33669c | |||
| 9c6ebad502 | |||
| cb1aa8bcf2 | |||
| 645115cb78 | |||
| 31f7a2e539 | |||
| 8b021356b8 |
14 changed files with 260 additions and 136 deletions
2
App.js
2
App.js
|
|
@ -16,6 +16,7 @@ import AprilFoolsManager from "./src/managers/AprilFoolsManager";
|
||||||
import Update from "./src/constants/Update";
|
import Update from "./src/constants/Update";
|
||||||
import ConnectionManager from "./src/managers/ConnectionManager";
|
import ConnectionManager from "./src/managers/ConnectionManager";
|
||||||
import URLHandler from "./src/utils/URLHandler";
|
import URLHandler from "./src/utils/URLHandler";
|
||||||
|
import {setSafeBounceHeight} from "react-navigation-collapsible";
|
||||||
|
|
||||||
type Props = {};
|
type Props = {};
|
||||||
|
|
||||||
|
|
@ -62,6 +63,7 @@ export default class App extends React.Component<Props, State> {
|
||||||
this.defaultData = {};
|
this.defaultData = {};
|
||||||
this.urlHandler = new URLHandler(this.onInitialURLParsed, this.onDetectURL);
|
this.urlHandler = new URLHandler(this.onInitialURLParsed, this.onDetectURL);
|
||||||
this.urlHandler.listen();
|
this.urlHandler.listen();
|
||||||
|
setSafeBounceHeight(Platform.OS === 'ios' ? 100 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
onInitialURLParsed = ({route, data}: Object) => {
|
onInitialURLParsed = ({route, data}: Object) => {
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,12 @@
|
||||||
"react": "16.9.0",
|
"react": "16.9.0",
|
||||||
"react-dom": "16.9.0",
|
"react-dom": "16.9.0",
|
||||||
"react-native": "https://github.com/expo/react-native/archive/sdk-37.0.0.tar.gz",
|
"react-native": "https://github.com/expo/react-native/archive/sdk-37.0.0.tar.gz",
|
||||||
|
"react-native-animatable": "^1.3.3",
|
||||||
"react-native-app-intro-slider": "^4.0.0",
|
"react-native-app-intro-slider": "^4.0.0",
|
||||||
"react-native-appearance": "~0.3.3",
|
"react-native-appearance": "~0.3.3",
|
||||||
"react-native-autolink": "^3.0.0",
|
"react-native-autolink": "^3.0.0",
|
||||||
"react-native-calendars": "^1.260.0",
|
"react-native-calendars": "^1.260.0",
|
||||||
|
"react-native-collapsible": "^1.5.2",
|
||||||
"react-native-gesture-handler": "~1.6.0",
|
"react-native-gesture-handler": "~1.6.0",
|
||||||
"react-native-image-modal": "^1.0.1",
|
"react-native-image-modal": "^1.0.1",
|
||||||
"react-native-modalize": "^1.3.6",
|
"react-native-modalize": "^1.3.6",
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,15 @@ import * as React from 'react';
|
||||||
import {StyleSheet, View} from "react-native";
|
import {StyleSheet, View} from "react-native";
|
||||||
import {FAB, IconButton, Surface, withTheme} from "react-native-paper";
|
import {FAB, IconButton, Surface, withTheme} from "react-native-paper";
|
||||||
import AutoHideComponent from "./AutoHideComponent";
|
import AutoHideComponent from "./AutoHideComponent";
|
||||||
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
|
||||||
|
const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
theme: Object,
|
theme: Object,
|
||||||
onPress: Function,
|
onPress: Function,
|
||||||
currentGroup: string,
|
seekAttention: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
|
@ -42,7 +45,7 @@ class AnimatedBottomBar extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: Props, nextState: State) {
|
shouldComponentUpdate(nextProps: Props, nextState: State) {
|
||||||
return (nextProps.currentGroup !== this.props.currentGroup)
|
return (nextProps.seekAttention !== this.props.seekAttention)
|
||||||
|| (nextState.currentMode !== this.state.currentMode);
|
|| (nextState.currentMode !== this.state.currentMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +78,17 @@ class AnimatedBottomBar extends React.Component<Props, State> {
|
||||||
ref={this.ref}
|
ref={this.ref}
|
||||||
style={styles.container}>
|
style={styles.container}>
|
||||||
<Surface style={styles.surface}>
|
<Surface style={styles.surface}>
|
||||||
|
<View style={styles.fabContainer}>
|
||||||
|
<AnimatedFAB
|
||||||
|
animation={this.props.seekAttention ? "jello" : undefined}
|
||||||
|
easing="ease-out"
|
||||||
|
iterationCount="infinite"
|
||||||
|
// useNativeDriver={true}
|
||||||
|
style={styles.fab}
|
||||||
|
icon="account-clock"
|
||||||
|
onPress={() => this.props.navigation.navigate('group-select')}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={{flexDirection: 'row'}}>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={this.displayModeIcons[this.state.currentMode]}
|
icon={this.displayModeIcons[this.state.currentMode]}
|
||||||
|
|
@ -86,13 +100,6 @@ class AnimatedBottomBar extends React.Component<Props, State> {
|
||||||
style={{marginLeft: 5}}
|
style={{marginLeft: 5}}
|
||||||
onPress={() => this.props.onPress('today', undefined)}/>
|
onPress={() => this.props.onPress('today', undefined)}/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.fabContainer}>
|
|
||||||
<FAB
|
|
||||||
style={styles.fab}
|
|
||||||
icon="account-clock"
|
|
||||||
onPress={() => this.props.navigation.navigate('group-select')}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={{flexDirection: 'row'}}>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon="chevron-left"
|
icon="chevron-left"
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export default class AutoHideComponent extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onScroll({nativeEvent}: Object) {
|
onScroll({nativeEvent}: Object) {
|
||||||
const speed = this.lastOffset - nativeEvent.contentOffset.y;
|
const speed = nativeEvent.contentOffset.y < 0 ? 0 : this.lastOffset - nativeEvent.contentOffset.y;
|
||||||
if (speed < -5) { // Go down
|
if (speed < -5) { // Go down
|
||||||
if (!this.isAnimationDownPlaying) {
|
if (!this.isAnimationDownPlaying) {
|
||||||
this.isAnimationDownPlaying = true;
|
this.isAnimationDownPlaying = true;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {StyleSheet, View} from "react-native";
|
||||||
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {ERROR_TYPE} from "../../utils/WebData";
|
import {ERROR_TYPE} from "../../utils/WebData";
|
||||||
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
|
@ -124,10 +125,15 @@ class ErrorView extends React.PureComponent<Props, State> {
|
||||||
render() {
|
render() {
|
||||||
this.generateMessage();
|
this.generateMessage();
|
||||||
return (
|
return (
|
||||||
<View style={{
|
<Animatable.View
|
||||||
|
style={{
|
||||||
...styles.outer,
|
...styles.outer,
|
||||||
backgroundColor: this.colors.background
|
backgroundColor: this.colors.background
|
||||||
}}>
|
}}
|
||||||
|
animation={"zoomIn"}
|
||||||
|
duration={200}
|
||||||
|
useNativeDriver
|
||||||
|
>
|
||||||
<View style={styles.inner}>
|
<View style={styles.inner}>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
|
|
@ -147,7 +153,7 @@ class ErrorView extends React.PureComponent<Props, State> {
|
||||||
: this.getRetryButton())
|
: this.getRetryButton())
|
||||||
: null}
|
: null}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</Animatable.View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class FeedItem extends React.Component<Props> {
|
||||||
const cardHeight = this.props.height - 2 * cardMargin;
|
const cardHeight = this.props.height - 2 * cardMargin;
|
||||||
const imageSize = 250;
|
const imageSize = 250;
|
||||||
const titleHeight = 80;
|
const titleHeight = 80;
|
||||||
const actionsHeight = 48;
|
const actionsHeight = 60;
|
||||||
const textHeight = hasImage
|
const textHeight = hasImage
|
||||||
? cardHeight - titleHeight - actionsHeight - imageSize
|
? cardHeight - titleHeight - actionsHeight - imageSize
|
||||||
: cardHeight - titleHeight - actionsHeight;
|
: cardHeight - titleHeight - actionsHeight;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {IconButton, List, withTheme} from 'react-native-paper';
|
import {List, withTheme} from 'react-native-paper';
|
||||||
import {FlatList} from "react-native";
|
import {FlatList, View} from "react-native";
|
||||||
import {stringMatchQuery} from "../../utils/Search";
|
import {stringMatchQuery} from "../../utils/Search";
|
||||||
|
import Collapsible from "react-native-collapsible";
|
||||||
|
import * as Animatable from "react-native-animatable";
|
||||||
|
import GroupListItem from "./GroupListItem";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
item: Object,
|
item: Object,
|
||||||
|
|
@ -20,14 +23,18 @@ type State = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const LIST_ITEM_HEIGHT = 64;
|
const LIST_ITEM_HEIGHT = 64;
|
||||||
|
const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon);
|
||||||
|
|
||||||
class GroupListAccordion extends React.Component<Props, State> {
|
class GroupListAccordion extends React.Component<Props, State> {
|
||||||
|
|
||||||
|
chevronRef: Object;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
expanded: props.item.id === "0",
|
expanded: props.item.id === "0",
|
||||||
}
|
}
|
||||||
|
this.chevronRef = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: Props, nextSate: State) {
|
shouldComponentUpdate(nextProps: Props, nextSate: State) {
|
||||||
|
|
@ -36,42 +43,27 @@ class GroupListAccordion extends React.Component<Props, State> {
|
||||||
|
|
||||||
return (nextProps.currentSearchString !== this.props.currentSearchString)
|
return (nextProps.currentSearchString !== this.props.currentSearchString)
|
||||||
|| (nextSate.expanded !== this.state.expanded)
|
|| (nextSate.expanded !== this.state.expanded)
|
||||||
|| (nextProps.favoriteNumber !== this.props.favoriteNumber);
|
|| (nextProps.favoriteNumber !== this.props.favoriteNumber)
|
||||||
|
|| (nextProps.item.content.length !== this.props.item.content.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
onPress = () => this.setState({expanded: !this.state.expanded});
|
onPress = () => {
|
||||||
|
this.chevronRef.current.transitionTo({rotate: this.state.expanded ? '0deg' : '180deg'});
|
||||||
|
this.setState({expanded: !this.state.expanded})
|
||||||
|
};
|
||||||
|
|
||||||
keyExtractor = (item: Object) => item.id.toString();
|
keyExtractor = (item: Object) => item.id.toString();
|
||||||
|
|
||||||
isItemFavorite(item: Object) {
|
|
||||||
return item.isFav !== undefined && item.isFav;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderItem = ({item}: Object) => {
|
renderItem = ({item}: Object) => {
|
||||||
if (stringMatchQuery(item.name, this.props.currentSearchString)) {
|
if (stringMatchQuery(item.name, this.props.currentSearchString)) {
|
||||||
|
|
||||||
const onPress = () => this.props.onGroupPress(item);
|
const onPress = () => this.props.onGroupPress(item);
|
||||||
const onStartPress = () => this.props.onFavoritePress(item);
|
const onStartPress = () => this.props.onFavoritePress(item);
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<GroupListItem
|
||||||
title={item.name}
|
height={LIST_ITEM_HEIGHT}
|
||||||
|
item={item}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
left={props =>
|
onStartPress={onStartPress}/>
|
||||||
<List.Icon
|
|
||||||
{...props}
|
|
||||||
icon={"chevron-right"}/>}
|
|
||||||
right={props =>
|
|
||||||
<IconButton
|
|
||||||
{...props}
|
|
||||||
icon={"star"}
|
|
||||||
onPress={onStartPress}
|
|
||||||
color={this.isItemFavorite(item) ? this.props.theme.colors.tetrisScore : props.color}
|
|
||||||
/>}
|
|
||||||
style={{
|
|
||||||
height: LIST_ITEM_HEIGHT,
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
} else
|
} else
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -81,36 +73,58 @@ class GroupListAccordion extends React.Component<Props, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const item = this.props.item;
|
const item = this.props.item;
|
||||||
|
const accordionColor = this.state.expanded
|
||||||
|
? this.props.theme.colors.primary
|
||||||
|
: this.props.theme.colors.text;
|
||||||
|
// console.log(item.id);
|
||||||
return (
|
return (
|
||||||
<List.Accordion
|
<View>
|
||||||
title={item.name}
|
<List.Item
|
||||||
expanded={this.state.expanded}
|
title={item.name}
|
||||||
onPress={this.onPress}
|
onPress={this.onPress}
|
||||||
style={{
|
style={{
|
||||||
height: this.props.height,
|
height: this.props.height,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
left={props =>
|
titleStyle={{color: accordionColor}}
|
||||||
item.id === "0"
|
left={props =>
|
||||||
? <List.Icon
|
item.id === "0"
|
||||||
|
? <List.Icon
|
||||||
|
{...props}
|
||||||
|
icon={"star"}
|
||||||
|
color={this.props.theme.colors.tetrisScore}
|
||||||
|
/>
|
||||||
|
: null}
|
||||||
|
right={(props) => <AnimatedListIcon
|
||||||
|
ref={this.chevronRef}
|
||||||
{...props}
|
{...props}
|
||||||
icon={"star"}
|
icon={"chevron-down"}
|
||||||
color={this.props.theme.colors.tetrisScore}
|
color={this.state.expanded
|
||||||
/>
|
? this.props.theme.colors.primary
|
||||||
: null}
|
: props.color
|
||||||
>
|
}
|
||||||
{/*$FlowFixMe*/}
|
useNativeDriver
|
||||||
<FlatList
|
/>}
|
||||||
data={item.content}
|
|
||||||
extraData={this.props.currentSearchString}
|
|
||||||
renderItem={this.renderItem}
|
|
||||||
keyExtractor={this.keyExtractor}
|
|
||||||
listKey={item.id}
|
|
||||||
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
|
|
||||||
// getItemLayout={this.itemLayout} // Broken with search
|
|
||||||
removeClippedSubviews={true}
|
|
||||||
/>
|
/>
|
||||||
</List.Accordion>
|
<Collapsible
|
||||||
|
collapsed={!this.state.expanded}
|
||||||
|
ease={"easeInOut"}
|
||||||
|
>
|
||||||
|
{this.state.expanded // Only render list if expanded for increased performance
|
||||||
|
? <FlatList
|
||||||
|
data={item.content}
|
||||||
|
extraData={this.props.currentSearchString}
|
||||||
|
renderItem={this.renderItem}
|
||||||
|
keyExtractor={this.keyExtractor}
|
||||||
|
listKey={item.id}
|
||||||
|
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
|
||||||
|
getItemLayout={this.itemLayout} // Broken with search
|
||||||
|
removeClippedSubviews={true}
|
||||||
|
/>
|
||||||
|
: null}
|
||||||
|
|
||||||
|
</Collapsible>
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
66
src/components/Lists/GroupListItem.js
Normal file
66
src/components/Lists/GroupListItem.js
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import {IconButton, List, withTheme} from 'react-native-paper';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
theme: Object,
|
||||||
|
onPress: Function,
|
||||||
|
onStartPress: Function,
|
||||||
|
item: Object,
|
||||||
|
height: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
isFav: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
class GroupListItem extends React.Component<Props, State> {
|
||||||
|
|
||||||
|
colors: Object;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.colors = props.theme.colors;
|
||||||
|
this.state = {
|
||||||
|
isFav: (props.item.isFav !== undefined && props.item.isFav),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(prevProps: Props, prevState: State) {
|
||||||
|
return (prevState.isFav !== this.state.isFav);
|
||||||
|
}
|
||||||
|
|
||||||
|
onStarPress = () => {
|
||||||
|
this.setState({isFav: !this.state.isFav});
|
||||||
|
this.props.onStartPress();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
title={this.props.item.name}
|
||||||
|
onPress={this.props.onPress}
|
||||||
|
left={props =>
|
||||||
|
<List.Icon
|
||||||
|
{...props}
|
||||||
|
icon={"chevron-right"}/>}
|
||||||
|
right={props =>
|
||||||
|
<IconButton
|
||||||
|
{...props}
|
||||||
|
icon={"star"}
|
||||||
|
onPress={this.onStarPress}
|
||||||
|
color={this.state.isFav
|
||||||
|
? this.props.theme.colors.tetrisScore
|
||||||
|
: props.color}
|
||||||
|
/>}
|
||||||
|
style={{
|
||||||
|
height: this.props.height,
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withTheme(GroupListItem);
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Avatar, List, ProgressBar, Surface, Text, withTheme} from 'react-native-paper';
|
import {ActivityIndicator, Avatar, List, ProgressBar, Surface, Text, withTheme} from 'react-native-paper';
|
||||||
import {StyleSheet, View} from "react-native";
|
import {StyleSheet, View} from "react-native";
|
||||||
import ProxiwashConstants from "../../constants/ProxiwashConstants";
|
import ProxiwashConstants from "../../constants/ProxiwashConstants";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import AprilFoolsManager from "../../managers/AprilFoolsManager";
|
import AprilFoolsManager from "../../managers/AprilFoolsManager";
|
||||||
|
import * as Animatable from "react-native-animatable";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
item: Object,
|
item: Object,
|
||||||
|
|
@ -13,6 +14,9 @@ type Props = {
|
||||||
height: number,
|
height: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AnimatedIcon = Animatable.createAnimatableComponent(Avatar.Icon);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component used to display a proxiwash item, showing machine progression and state
|
* Component used to display a proxiwash item, showing machine progression and state
|
||||||
*/
|
*/
|
||||||
|
|
@ -83,14 +87,21 @@ class ProxiwashListItem extends React.Component<Props> {
|
||||||
: 1;
|
: 1;
|
||||||
|
|
||||||
const icon = props.isWatched
|
const icon = props.isWatched
|
||||||
? <Avatar.Icon
|
? <AnimatedIcon
|
||||||
icon={'bell-ring'}
|
icon={'bell-ring'}
|
||||||
size={45}
|
animation={"rubberBand"}
|
||||||
|
useNativeDriver
|
||||||
|
size={50}
|
||||||
color={colors.primary}
|
color={colors.primary}
|
||||||
style={styles.icon}
|
style={styles.icon}
|
||||||
/>
|
/>
|
||||||
: <Avatar.Icon
|
: <AnimatedIcon
|
||||||
icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'}
|
icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'}
|
||||||
|
animation={isRunning ? "flash" : undefined}
|
||||||
|
iterationCount={"infinite"}
|
||||||
|
easing={"linear"}
|
||||||
|
duration={2000}
|
||||||
|
useNativeDriver
|
||||||
size={40}
|
size={40}
|
||||||
color={colors.text}
|
color={colors.text}
|
||||||
style={styles.icon}
|
style={styles.icon}
|
||||||
|
|
@ -136,12 +147,20 @@ class ProxiwashListItem extends React.Component<Props> {
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={{justifyContent: 'center',}}>
|
<View style={{justifyContent: 'center',}}>
|
||||||
<Avatar.Icon
|
{
|
||||||
icon={stateIcon}
|
isRunning
|
||||||
color={colors.text}
|
? <ActivityIndicator
|
||||||
size={30}
|
animating={true}
|
||||||
style={styles.icon}
|
size={'small'}
|
||||||
/>
|
style={{marginLeft: 10}}/>
|
||||||
|
: <Avatar.Icon
|
||||||
|
icon={stateIcon}
|
||||||
|
color={colors.text}
|
||||||
|
size={30}
|
||||||
|
style={styles.icon}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
</View>)}
|
</View>)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {Animated, RefreshControl, View} from "react-native";
|
||||||
import ErrorView from "../Custom/ErrorView";
|
import ErrorView from "../Custom/ErrorView";
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
import {withCollapsible} from "../../utils/withCollapsible";
|
import {withCollapsible} from "../../utils/withCollapsible";
|
||||||
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
|
@ -174,9 +175,29 @@ class WebSectionList extends React.PureComponent<Props, State> {
|
||||||
index
|
index
|
||||||
});
|
});
|
||||||
|
|
||||||
onListScroll= (event) => {
|
renderSectionHeader = (data: Object) => {
|
||||||
|
return (
|
||||||
|
<Animatable.View
|
||||||
|
animation={"fadeInUp"}
|
||||||
|
duration={500}
|
||||||
|
useNativeDriver
|
||||||
|
>
|
||||||
|
{this.props.renderSectionHeader(data)}
|
||||||
|
</Animatable.View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
renderItem = (data: Object) => {
|
||||||
|
return (
|
||||||
|
<Animatable.View
|
||||||
|
animation={"fadeInUp"}
|
||||||
|
duration={500}
|
||||||
|
useNativeDriver
|
||||||
|
>
|
||||||
|
{this.props.renderItem(data)}
|
||||||
|
</Animatable.View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let dataset = [];
|
let dataset = [];
|
||||||
|
|
@ -198,9 +219,9 @@ class WebSectionList extends React.PureComponent<Props, State> {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
//$FlowFixMe
|
//$FlowFixMe
|
||||||
renderSectionHeader={shouldRenderHeader ? this.props.renderSectionHeader : this.getEmptySectionHeader}
|
renderSectionHeader={shouldRenderHeader ? this.renderSectionHeader : this.getEmptySectionHeader}
|
||||||
//$FlowFixMe
|
//$FlowFixMe
|
||||||
renderItem={this.props.renderItem}
|
renderItem={this.renderItem}
|
||||||
stickySectionHeadersEnabled={this.props.stickyHeader}
|
stickySectionHeadersEnabled={this.props.stickyHeader}
|
||||||
style={{minHeight: '100%'}}
|
style={{minHeight: '100%'}}
|
||||||
ListEmptyComponent={this.state.refreshing
|
ListEmptyComponent={this.state.refreshing
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,6 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
onOpenClicked = () => Linking.openURL(this.props.url);
|
onOpenClicked = () => Linking.openURL(this.props.url);
|
||||||
|
|
||||||
injectJavaScript = (script: string) => {
|
injectJavaScript = (script: string) => {
|
||||||
// console.log(this.webviewRef.current.getNode().webViewRef.current);
|
|
||||||
this.webviewRef.current.getNode().injectJavaScript(script);
|
this.webviewRef.current.getNode().injectJavaScript(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,46 +124,21 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
*/
|
*/
|
||||||
getRenderLoading = () => <BasicLoadingScreen isAbsolute={true}/>;
|
getRenderLoading = () => <BasicLoadingScreen isAbsolute={true}/>;
|
||||||
|
|
||||||
// document.getElementsByTagName('body')[0].style.paddingTop = '100px';
|
|
||||||
|
|
||||||
// $( 'body *' ).filter(function(){
|
|
||||||
// var position = $(this).css('position');
|
|
||||||
// var top = $(this).css('top');
|
|
||||||
// if((position === 'fixed') && top !== 'auto'){
|
|
||||||
// console.log(top);
|
|
||||||
// $(this).css('top', 'calc(' + top + ' + 100px)');
|
|
||||||
// console.log($(this).css('top'));
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
|
|
||||||
// document.querySelectorAll('body *').forEach(function(node){
|
|
||||||
// var style = window.getComputedStyle(node);
|
|
||||||
// var position = style.getPropertyValue('position');
|
|
||||||
// var top = style.getPropertyValue('top');
|
|
||||||
// if((position === 'fixed') && top !== 'auto'){
|
|
||||||
// console.log(top);
|
|
||||||
// node.style.top = 'calc(' + top + ' + 100px)';
|
|
||||||
// console.log(node.style.top);
|
|
||||||
// console.log(node);
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
|
|
||||||
getJavascriptPadding(padding: number) {
|
getJavascriptPadding(padding: number) {
|
||||||
return (
|
return (
|
||||||
"document.getElementsByTagName('body')[0].style.paddingTop = '" + padding + "px';\n" +
|
"document.getElementsByTagName('body')[0].style.paddingTop = '" + padding + "px';" +
|
||||||
"true;"
|
"true;"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {containerPaddingTop, onScrollWithListener} = this.props.collapsibleStack;
|
const {containerPaddingTop, onScrollWithListener} = this.props.collapsibleStack;
|
||||||
const customJS = this.getJavascriptPadding(containerPaddingTop);
|
|
||||||
return (
|
return (
|
||||||
<AnimatedWebView
|
<AnimatedWebView
|
||||||
ref={this.webviewRef}
|
ref={this.webviewRef}
|
||||||
source={{uri: this.props.url}}
|
source={{uri: this.props.url}}
|
||||||
startInLoadingState={true}
|
startInLoadingState={true}
|
||||||
injectedJavaScript={this.props.customJS + customJS}
|
injectedJavaScript={this.props.customJS}
|
||||||
javaScriptEnabled={true}
|
javaScriptEnabled={true}
|
||||||
renderLoading={this.getRenderLoading}
|
renderLoading={this.getRenderLoading}
|
||||||
renderError={() => <ErrorView
|
renderError={() => <ErrorView
|
||||||
|
|
@ -175,6 +149,7 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
this.canGoBack = navState.canGoBack;
|
this.canGoBack = navState.canGoBack;
|
||||||
}}
|
}}
|
||||||
onMessage={this.props.onMessage}
|
onMessage={this.props.onMessage}
|
||||||
|
onLoad={() => this.injectJavaScript(this.getJavascriptPadding(containerPaddingTop))}
|
||||||
// Animations
|
// Animations
|
||||||
onScroll={onScrollWithListener(this.props.onScroll)}
|
onScroll={onScrollWithListener(this.props.onScroll)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {FlatList} from "react-native";
|
import {FlatList, View} from "react-native";
|
||||||
import {Drawer, List, withTheme} from 'react-native-paper';
|
import {Drawer, List, withTheme} from 'react-native-paper';
|
||||||
import {Linking} from "expo";
|
import {Linking} from "expo";
|
||||||
|
import Collapsible from "react-native-collapsible";
|
||||||
|
import * as Animatable from "react-native-animatable";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
|
@ -19,6 +21,8 @@ type State = {
|
||||||
expanded: boolean
|
expanded: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon);
|
||||||
|
|
||||||
const LIST_ITEM_HEIGHT = 48;
|
const LIST_ITEM_HEIGHT = 48;
|
||||||
|
|
||||||
class SideBarSection extends React.PureComponent<Props, State> {
|
class SideBarSection extends React.PureComponent<Props, State> {
|
||||||
|
|
@ -29,10 +33,12 @@ class SideBarSection extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
colors: Object;
|
colors: Object;
|
||||||
shouldExpand: boolean;
|
shouldExpand: boolean;
|
||||||
|
chevronRef: Object;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.colors = props.theme.colors;
|
this.colors = props.theme.colors;
|
||||||
|
this.chevronRef = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -104,8 +110,10 @@ class SideBarSection extends React.PureComponent<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleAccordion = () => {
|
toggleAccordion = () => {
|
||||||
if ((!this.state.expanded && this.shouldExpand) || !this.shouldExpand)
|
if ((!this.state.expanded && this.shouldExpand) || !this.shouldExpand) {
|
||||||
|
this.chevronRef.current.transitionTo({ rotate: this.state.expanded ? '0deg' : '180deg' });
|
||||||
this.setState({expanded: !this.state.expanded})
|
this.setState({expanded: !this.state.expanded})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldRenderAccordion() {
|
shouldRenderAccordion() {
|
||||||
|
|
@ -140,13 +148,22 @@ class SideBarSection extends React.PureComponent<Props, State> {
|
||||||
if (this.shouldExpand)
|
if (this.shouldExpand)
|
||||||
this.toggleAccordion();
|
this.toggleAccordion();
|
||||||
return (
|
return (
|
||||||
<List.Accordion
|
<View>
|
||||||
title={this.props.sectionName}
|
<List.Item
|
||||||
expanded={this.state.expanded}
|
title={this.props.sectionName}
|
||||||
onPress={this.toggleAccordion}
|
// expanded={this.state.expanded}
|
||||||
>
|
onPress={this.toggleAccordion}
|
||||||
{this.getFlatList()}
|
right={(props) => <AnimatedListIcon
|
||||||
</List.Accordion>
|
ref={this.chevronRef}
|
||||||
|
{...props}
|
||||||
|
icon={"chevron-down"}
|
||||||
|
useNativeDriver
|
||||||
|
/>}
|
||||||
|
/>
|
||||||
|
<Collapsible collapsed={!this.state.expanded}>
|
||||||
|
{this.getFlatList()}
|
||||||
|
</Collapsible>
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
} else
|
} else
|
||||||
return this.getFlatList();
|
return this.getFlatList();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Platform, View} from "react-native";
|
import {Platform} from "react-native";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import {Searchbar, withTheme} from "react-native-paper";
|
import {Searchbar, withTheme} from "react-native-paper";
|
||||||
import {stringMatchQuery} from "../utils/Search";
|
import {stringMatchQuery} from "../utils/Search";
|
||||||
|
|
@ -206,20 +206,16 @@ class GroupSelectionScreen extends React.Component<Props, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={{
|
<WebSectionList
|
||||||
height: '100%'
|
{...this.props}
|
||||||
}}>
|
createDataset={this.createDataset}
|
||||||
<WebSectionList
|
autoRefreshTime={0}
|
||||||
{...this.props}
|
refreshOnFocus={false}
|
||||||
createDataset={this.createDataset}
|
fetchUrl={GROUPS_URL}
|
||||||
autoRefreshTime={0}
|
renderItem={this.renderItem}
|
||||||
refreshOnFocus={false}
|
updateData={this.state.currentSearchString + this.state.favoriteGroups.length}
|
||||||
fetchUrl={GROUPS_URL}
|
itemHeight={LIST_ITEM_HEIGHT}
|
||||||
renderItem={this.renderItem}
|
/>
|
||||||
updateData={this.state.currentSearchString + this.state.favoriteGroups.length}
|
|
||||||
itemHeight={LIST_ITEM_HEIGHT}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,6 @@ class PlanexScreen extends React.Component<Props, State> {
|
||||||
'removeAlpha();'
|
'removeAlpha();'
|
||||||
+ '});'
|
+ '});'
|
||||||
+ EXEC_COMMAND
|
+ EXEC_COMMAND
|
||||||
+ "function cc(msg) {alert(msg)};"
|
|
||||||
+ 'true;'; // Prevents crash on ios
|
+ 'true;'; // Prevents crash on ios
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -336,7 +335,7 @@ class PlanexScreen extends React.Component<Props, State> {
|
||||||
{...this.props}
|
{...this.props}
|
||||||
ref={this.barRef}
|
ref={this.barRef}
|
||||||
onPress={this.sendMessage}
|
onPress={this.sendMessage}
|
||||||
currentGroup={this.state.currentGroup.name}
|
seekAttention={this.state.currentGroup.id === -1}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue