Pull to Refresh: Notifications, Chats, Bookmarks
This commit is contained in:
parent
f61845b876
commit
65a2a40cb2
|
@ -9,15 +9,17 @@ export const BOOKMARKED_STATUSES_EXPAND_REQUEST = 'BOOKMARKED_STATUSES_EXPAND_RE
|
||||||
export const BOOKMARKED_STATUSES_EXPAND_SUCCESS = 'BOOKMARKED_STATUSES_EXPAND_SUCCESS';
|
export const BOOKMARKED_STATUSES_EXPAND_SUCCESS = 'BOOKMARKED_STATUSES_EXPAND_SUCCESS';
|
||||||
export const BOOKMARKED_STATUSES_EXPAND_FAIL = 'BOOKMARKED_STATUSES_EXPAND_FAIL';
|
export const BOOKMARKED_STATUSES_EXPAND_FAIL = 'BOOKMARKED_STATUSES_EXPAND_FAIL';
|
||||||
|
|
||||||
|
const noOp = () => new Promise(f => f());
|
||||||
|
|
||||||
export function fetchBookmarkedStatuses() {
|
export function fetchBookmarkedStatuses() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
|
if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
|
||||||
return;
|
return dispatch(noOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(fetchBookmarkedStatusesRequest());
|
dispatch(fetchBookmarkedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/bookmarks').then(response => {
|
return api(getState).get('/api/v1/bookmarks').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
|
@ -53,12 +55,12 @@ export function expandBookmarkedStatuses() {
|
||||||
const url = getState().getIn(['status_lists', 'bookmarks', 'next'], null);
|
const url = getState().getIn(['status_lists', 'bookmarks', 'next'], null);
|
||||||
|
|
||||||
if (url === null || getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
|
if (url === null || getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
|
||||||
return;
|
return dispatch(noOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(expandBookmarkedStatusesRequest());
|
dispatch(expandBookmarkedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
return api(getState).get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import Column from '../ui/components/column';
|
import Column from 'soapbox/components/column';
|
||||||
|
import SubNavigation from 'soapbox/components/sub_navigation';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import StatusList from '../../components/status_list';
|
import StatusList from '../../components/status_list';
|
||||||
|
@ -38,15 +39,22 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
fetchData = () => {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
dispatch(fetchBookmarkedStatuses());
|
return dispatch(fetchBookmarkedStatuses());
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadMore = debounce(() => {
|
handleLoadMore = debounce(() => {
|
||||||
this.props.dispatch(expandBookmarkedStatuses());
|
this.props.dispatch(expandBookmarkedStatuses());
|
||||||
}, 300, { leading: true })
|
}, 300, { leading: true })
|
||||||
|
|
||||||
|
handleRefresh = () => {
|
||||||
|
return this.fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
||||||
|
@ -55,7 +63,8 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.bookmarks' defaultMessage="You don't have any bookmarks yet. When you add one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.bookmarks' defaultMessage="You don't have any bookmarks yet. When you add one, it will show up here." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column heading={intl.formatMessage(messages.heading)} transparent>
|
<Column transparent>
|
||||||
|
<SubNavigation message={intl.formatMessage(messages.heading)} />
|
||||||
<StatusList
|
<StatusList
|
||||||
trackScroll={!pinned}
|
trackScroll={!pinned}
|
||||||
statusIds={statusIds}
|
statusIds={statusIds}
|
||||||
|
@ -63,6 +72,7 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
|
onRefresh={this.handleRefresh}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
shouldUpdateScroll={shouldUpdateScroll}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
|
|
|
@ -3,12 +3,12 @@ import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Column from '../../components/column';
|
import Column from '../../components/column';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
import { launchChat } from 'soapbox/actions/chats';
|
import { fetchChats, launchChat } from 'soapbox/actions/chats';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ChatList from './components/chat_list';
|
import ChatList from './components/chat_list';
|
||||||
import AudioToggle from 'soapbox/features/chats/components/audio_toggle';
|
import AudioToggle from 'soapbox/features/chats/components/audio_toggle';
|
||||||
import AccountSearch from 'soapbox/components/account_search';
|
import AccountSearch from 'soapbox/components/account_search';
|
||||||
import Pullable from 'soapbox/components/pullable';
|
import PullToRefresh from 'soapbox/components/pull_to_refresh';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
title: { id: 'column.chats', defaultMessage: 'Chats' },
|
title: { id: 'column.chats', defaultMessage: 'Chats' },
|
||||||
|
@ -36,6 +36,11 @@ class ChatIndex extends React.PureComponent {
|
||||||
this.context.router.history.push(`/chats/${chat.get('id')}`);
|
this.context.router.history.push(`/chats/${chat.get('id')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleRefresh = () => {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
return dispatch(fetchChats());
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
|
|
||||||
|
@ -55,12 +60,12 @@ class ChatIndex extends React.PureComponent {
|
||||||
onSelected={this.handleSuggestion}
|
onSelected={this.handleSuggestion}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Pullable>
|
<PullToRefresh onRefresh={this.handleRefresh}>
|
||||||
<ChatList
|
<ChatList
|
||||||
onClickChat={this.handleClickChat}
|
onClickChat={this.handleClickChat}
|
||||||
emptyMessage={<FormattedMessage id='chat_panels.main_window.empty' defaultMessage="No chats found. To start a chat, visit a user's profile." />}
|
emptyMessage={<FormattedMessage id='chat_panels.main_window.empty' defaultMessage="No chats found. To start a chat, visit a user's profile." />}
|
||||||
/>
|
/>
|
||||||
</Pullable>
|
</PullToRefresh>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import TimelineQueueButtonHeader from '../../components/timeline_queue_button_h
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
import { getSettings } from 'soapbox/actions/settings';
|
||||||
import PlaceholderNotification from 'soapbox/features/placeholder/components/placeholder_notification';
|
import PlaceholderNotification from 'soapbox/features/placeholder/components/placeholder_notification';
|
||||||
import SubNavigation from 'soapbox/components/sub_navigation';
|
import SubNavigation from 'soapbox/components/sub_navigation';
|
||||||
import Pullable from 'soapbox/components/pullable';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
title: { id: 'column.notifications', defaultMessage: 'Notifications' },
|
title: { id: 'column.notifications', defaultMessage: 'Notifications' },
|
||||||
|
@ -129,6 +128,11 @@ class Notifications extends React.PureComponent {
|
||||||
this.props.dispatch(dequeueNotifications());
|
this.props.dispatch(dequeueNotifications());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleRefresh = () => {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
return dispatch(expandNotifications());
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl, notifications, isLoading, hasMore, showFilterBar, totalQueuedNotificationsCount } = this.props;
|
const { intl, notifications, isLoading, hasMore, showFilterBar, totalQueuedNotificationsCount } = this.props;
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
|
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
|
||||||
|
@ -173,6 +177,7 @@ class Notifications extends React.PureComponent {
|
||||||
placeholderComponent={PlaceholderNotification}
|
placeholderComponent={PlaceholderNotification}
|
||||||
placeholderCount={20}
|
placeholderCount={20}
|
||||||
onLoadMore={this.handleLoadOlder}
|
onLoadMore={this.handleLoadOlder}
|
||||||
|
onRefresh={this.handleRefresh}
|
||||||
onScrollToTop={this.handleScrollToTop}
|
onScrollToTop={this.handleScrollToTop}
|
||||||
onScroll={this.handleScroll}
|
onScroll={this.handleScroll}
|
||||||
>
|
>
|
||||||
|
@ -189,9 +194,7 @@ class Notifications extends React.PureComponent {
|
||||||
count={totalQueuedNotificationsCount}
|
count={totalQueuedNotificationsCount}
|
||||||
message={messages.queue}
|
message={messages.queue}
|
||||||
/>
|
/>
|
||||||
<Pullable>
|
|
||||||
{scrollContainer}
|
{scrollContainer}
|
||||||
</Pullable>
|
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -934,11 +934,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make MaterialStatus flush against SubNavigation
|
// Make MaterialStatus flush against SubNavigation
|
||||||
.sub-navigation ~ .slist .item-list > article:first-child .material-status__status,
|
.sub-navigation ~,
|
||||||
.sub-navigation ~ .material-status:not(.material-status + .material-status) .material-status__status {
|
.sub-navigation ~ .ptr > .ptr__children > {
|
||||||
|
// ScrollableList
|
||||||
|
.slist .item-list > article:first-child,
|
||||||
|
// Thread
|
||||||
|
.material-status:not(.material-status + .material-status) {
|
||||||
|
// MaterialStatus
|
||||||
|
.material-status__status {
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Display background for loading indicator
|
// Display background for loading indicator
|
||||||
.column--transparent {
|
.column--transparent {
|
||||||
|
|
Loading…
Reference in New Issue