Merge pull request #427 from esteemapp/bugfix/notification-loading

Bugfix/notification loading
This commit is contained in:
uğur erdal 2019-01-15 19:21:43 +03:00 committed by GitHub
commit e794585e27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 150 additions and 47 deletions

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import {
View, Text, Image, StyleSheet,
View, Image, StyleSheet,
} from 'react-native';
const styles = StyleSheet.create({
@ -35,25 +35,25 @@ export default class PulseAnimation extends Component {
},
};
mounted = true;
constructor(props) {
super(props);
this.state = {
color: this.props.color,
duration: this.props.duration,
image: this.props.image,
maxDiameter: this.props.diameter,
numPulses: this.props.numPulses,
color: props.color,
duration: props.duration,
image: props.image,
maxDiameter: props.diameter,
numPulses: props.numPulses,
pulses: [],
pulseStyle: this.props.pulseStyle,
speed: this.props.speed,
pulseStyle: props.pulseStyle,
speed: props.speed,
started: false,
style: this.props.style,
style: props.style,
};
}
mounted = true;
componentDidMount() {
const { numPulses, duration, speed } = this.state;
@ -79,15 +79,16 @@ export default class PulseAnimation extends Component {
clearInterval(this.timer);
}
createPulse = (pKey) => {
createPulse = () => {
if (this.mounted) {
const pulses = this.state.pulses;
const { pulses, maxDiameter } = this.state;
const { initialDiameter } = this.props;
const pulse = {
pulseKey: pulses.length + 1,
diameter: this.props.initialDiameter,
diameter: initialDiameter,
opacity: 0.5,
centerOffset: (this.state.maxDiameter - this.props.initialDiameter) / 2,
centerOffset: (maxDiameter - initialDiameter) / 2,
};
pulses.push(pulse);
@ -99,10 +100,10 @@ export default class PulseAnimation extends Component {
updatePulse = () => {
if (this.mounted) {
const pulses = this.state.pulses.map((p, i) => {
const maxDiameter = this.state.maxDiameter;
const { maxDiameter } = this.state;
const newDiameter = p.diameter > maxDiameter ? 0 : p.diameter + 2;
const centerOffset = (maxDiameter - newDiameter) / 2;
const opacity = Math.abs(newDiameter / this.state.maxDiameter - 1);
const opacity = Math.abs(newDiameter / maxDiameter - 1);
const pulse = {
pulseKey: i + 1,

View File

@ -22,7 +22,6 @@ const FilterBarView = ({
iconSize,
isHide,
onDropdownSelect,
pageType,
onRightIconPress,
options,
rightIconName,

View File

@ -5,4 +5,17 @@ export default EStyleSheet.create({
backgroundColor: '$primaryBackgroundColor',
flex: 1,
},
flatlistFooter: {
alignContent: 'center',
alignItems: 'center',
marginTop: 10,
marginBottom: 40,
borderColor: '$borderColor',
},
loading: {
alignContent: 'center',
alignItems: 'center',
justifyContent: 'center',
flex: 1,
},
});

View File

@ -1,5 +1,7 @@
import React, { PureComponent, Fragment } from 'react';
import { View, ScrollView, FlatList } from 'react-native';
import {
View, ScrollView, FlatList, ActivityIndicator, RefreshControl,
} from 'react-native';
import { injectIntl } from 'react-intl';
// Constants
@ -34,6 +36,7 @@ class NotificationView extends PureComponent {
{ key: 'follows', value: 'FOLLOWS' },
{ key: 'reblogs', value: 'REBLOGS' },
],
selectedFilter: null,
};
}
@ -45,7 +48,8 @@ class NotificationView extends PureComponent {
const { getActivities } = this.props;
const { filters } = this.state;
getActivities(filters[index].key);
this.setState({ selectedFilter: filters[index].key });
getActivities(filters[index].key, false);
};
_renderList = (data) => {
@ -60,11 +64,25 @@ class NotificationView extends PureComponent {
handleOnPressNotification={navigateToNotificationRoute}
/>
)}
initialNumToRender={data.length}
maxToRenderPerBatch={data.length}
keyExtractor={item => item.id}
/>
);
};
_renderFooterLoading = () => {
const { loading, notifications } = this.props;
if (loading && notifications.length > 0) {
return (
<View style={styles.flatlistFooter}>
<ActivityIndicator animating size="large" />
</View>
);
}
return null;
};
_getNotificationsArrays = () => {
const { notifications, intl } = this.props;
@ -113,31 +131,39 @@ class NotificationView extends PureComponent {
};
_getTimeListIndex = (timestamp) => {
if (isToday(timestamp)) {
return 0;
}
if (isToday(timestamp)) return 0;
if (isYesterday(timestamp)) {
return 1;
}
if (isYesterday(timestamp)) return 1;
if (isThisWeek(timestamp)) {
return 2;
}
if (isThisWeek(timestamp)) return 2;
if (isThisMonth(timestamp)) {
return 3;
}
if (isThisMonth(timestamp)) return 3;
return 4;
};
_getActivityIndicator = () => (
<View style={styles.loading}>
<ActivityIndicator animating size="large" />
</View>
);
render() {
const { readAllNotification } = this.props;
const { filters } = this.state;
const {
readAllNotification,
getActivities,
loading,
readAllNotificationLoading,
isDarkTheme,
} = this.props;
const { filters, selectedFilter } = this.state;
const _notifications = this._getNotificationsArrays();
if (_notifications.length === 0) {
return this._getActivityIndicator();
}
return (
<View style={styles.container}>
<FilterBar
@ -145,12 +171,36 @@ class NotificationView extends PureComponent {
options={filters.map(item => item.value)}
defaultText="ALL ACTIVITIES"
onDropdownSelect={this._handleOnDropdownSelect}
rightIconName="ios-checkmark"
rightIconName="check"
rightIconType="MaterialIcons"
onRightIconPress={readAllNotification}
/>
<ScrollView style={styles.scrollView}>
<ScrollView
style={styles.scrollView}
onScroll={(e) => {
let paddingToBottom = 1;
paddingToBottom += e.nativeEvent.layoutMeasurement.height;
if (
e.nativeEvent.contentOffset.y >= e.nativeEvent.contentSize.height - paddingToBottom
&& !loading
) {
getActivities(selectedFilter, true);
}
}}
>
<FlatList
data={_notifications}
refreshing={readAllNotificationLoading}
onRefresh={() => null}
refreshControl={(
<RefreshControl
refreshing={readAllNotificationLoading}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
colors={['#fff']}
/>
)}
renderItem={({ item, index }) => (
<Fragment>
<ContainerHeader
@ -163,6 +213,7 @@ class NotificationView extends PureComponent {
</Fragment>
)}
keyExtractor={item => item.title}
ListFooterComponent={this._renderFooterLoading}
/>
</ScrollView>
</View>

View File

@ -26,10 +26,10 @@ class NotificationLineView extends PureComponent {
// Component Life Cycles
componentWillReceiveProps(nextProps) {
const { isRead } = this.props;
const { notification } = this.props;
if (isRead !== nextProps.isRead) {
this.setState({ isRead: nextProps.isRead });
if (notification.read !== nextProps.notification.read) {
this.setState({ isRead: nextProps.notification.read });
}
}

View File

@ -16,9 +16,20 @@ class NotificationContainer extends Component {
super(props);
this.state = {
notifications: [],
lastNotificationId: null,
notificationLoading: false,
readAllNotificationLoading: false,
};
}
componentDidMount() {
const { username } = this.props;
if (username) {
this._getAvtivities();
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.activeBottomTab === ROUTES.TABBAR.NOTIFICATION) {
if (nextProps.username) {
@ -27,12 +38,24 @@ class NotificationContainer extends Component {
}
}
_getAvtivities = (type = null) => {
_getAvtivities = (type = null, loadMore = false) => {
const { username } = this.props;
const { lastNotificationId, notifications } = this.state;
const since = loadMore ? lastNotificationId : null;
getActivities({ user: username, type }).then((res) => {
this.setState({ notifications: res });
});
this.setState({ notificationLoading: true });
getActivities({ user: username, type, since })
.then((res) => {
const lastId = [...res].pop().id;
this.setState({
notifications: loadMore ? [...notifications, ...res] : res,
lastNotificationId: lastId,
notificationLoading: false,
});
})
.catch(() => this.setState({ notificationLoading: false }));
};
_navigateToNotificationRoute = (data) => {
@ -65,10 +88,13 @@ class NotificationContainer extends Component {
_readAllNotification = () => {
const { username, dispatch } = this.props;
const { notifications } = this.state;
this.setState({ readAllNotificationLoading: true });
markActivityAsRead(username).then((result) => {
dispatch(updateUnreadActivityCount(result.unread));
const updatedNotifications = notifications.map(item => ({ ...item, read: 1 }));
this.setState({ notifications: updatedNotifications });
this.setState({ notifications: updatedNotifications, readAllNotificationLoading: false });
});
};
@ -79,24 +105,32 @@ class NotificationContainer extends Component {
};
render() {
const { notifications } = this.state;
const { isLoggedIn } = this.props;
const {
notifications, notificationLoading, readAllNotificationLoading, isDarkTheme,
} = this.state;
return (
<NotificationScreen
getActivities={this._getAvtivities}
notifications={notifications}
isDarkTheme={isDarkTheme}
navigateToNotificationRoute={this._navigateToNotificationRoute}
readAllNotification={this._readAllNotification}
handleLoginPress={this._handleOnPressLogin}
{...this.props}
notificationLoading={notificationLoading}
readAllNotificationLoading={readAllNotificationLoading}
isLoggedIn={isLoggedIn}
/>
);
}
}
const mapStateToProps = state => ({
username: state.account.currentAccount.name,
isLoggedIn: state.application.isLoggedIn,
isDarkTheme: state.application.isDarkTheme,
username: state.account.currentAccount.name,
activeBottomTab: state.ui.activeBottomTab,
});

View File

@ -29,7 +29,10 @@ class NotificationScreen extends PureComponent {
readAllNotification,
handleLoginPress,
isLoggedIn,
notificationLoading,
readAllNotificationLoading,
} = this.props;
return (
<View style={styles.container}>
<Header />
@ -51,6 +54,8 @@ class NotificationScreen extends PureComponent {
notifications={notifications}
navigateToNotificationRoute={navigateToNotificationRoute}
readAllNotification={readAllNotification}
readAllNotificationLoading={readAllNotificationLoading}
loading={notificationLoading}
/>
) : (
<NoPost