From 16f66446029d56c60da73db586cca208f584d797 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 4 Nov 2021 12:34:22 -0500 Subject: [PATCH 1/6] Status: implement actual Pull to Refresh on threads, add PullToRefresh custom component --- app/soapbox/components/pull_to_refresh.js | 43 +++++++++++++++++++++++ app/soapbox/components/pullable.js | 7 +--- app/soapbox/features/status/index.js | 20 ++++++++--- 3 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 app/soapbox/components/pull_to_refresh.js diff --git a/app/soapbox/components/pull_to_refresh.js b/app/soapbox/components/pull_to_refresh.js new file mode 100644 index 000000000..6d315e687 --- /dev/null +++ b/app/soapbox/components/pull_to_refresh.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import PTRComponent from 'react-simple-pull-to-refresh'; + +/** + * PullToRefresh: + * Wrapper around a third-party PTR component with Soapbox defaults. + */ +export default class PullToRefresh extends React.Component { + + static propTypes = { + children: PropTypes.node.isRequired, + onRefresh: PropTypes.func, + } + + handleRefresh = () => { + const { onRefresh } = this.props; + + if (onRefresh) { + return onRefresh(); + } else { + // If not provided, do nothing + return new Promise(resolve => resolve()); + } + } + + render() { + const { children, onRefresh, ...rest } = this.props; + + return ( + + {children} + + ); + } + +} diff --git a/app/soapbox/components/pullable.js b/app/soapbox/components/pullable.js index 52970dd7d..3d2f83edb 100644 --- a/app/soapbox/components/pullable.js +++ b/app/soapbox/components/pullable.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import PullToRefresh from 'react-simple-pull-to-refresh'; +import PullToRefresh from './pull_to_refresh'; /** * Pullable: @@ -13,16 +13,11 @@ export default class Pullable extends React.Component { children: PropTypes.node.isRequired, } - handleRefresh = () => { - return new Promise(resolve => resolve()); - } - render() { const { children } = this.props; return ( diff --git a/app/soapbox/features/status/index.js b/app/soapbox/features/status/index.js index 1c887da96..e56ab3d37 100644 --- a/app/soapbox/features/status/index.js +++ b/app/soapbox/features/status/index.js @@ -52,7 +52,7 @@ import ThreadStatus from './components/thread_status'; import PendingStatus from 'soapbox/features/ui/components/pending_status'; import SubNavigation from 'soapbox/components/sub_navigation'; import { launchChat } from 'soapbox/actions/chats'; -import Pullable from 'soapbox/components/pullable'; +import PullToRefresh from 'soapbox/components/pull_to_refresh'; const messages = defineMessages({ title: { id: 'status.title', defaultMessage: 'Post' }, @@ -167,8 +167,13 @@ class Status extends ImmutablePureComponent { emojiSelectorFocused: false, }; + fetchStatus = () => { + const { dispatch, params } = this.props; + return dispatch(fetchStatus(params.statusId)); + } + componentDidMount() { - this.props.dispatch(fetchStatus(this.props.params.statusId)); + this.fetchStatus(); attachFullscreenListener(this.onFullScreenChange); } @@ -564,6 +569,13 @@ class Status extends ImmutablePureComponent { this.setState({ fullscreen: isFullscreen() }); } + handleRefresh = () => { + return new Promise(resolve => { + this.fetchStatus(); + resolve(); + }); + } + render() { let ancestors, descendants; const { status, ancestorsIds, descendantsIds, intl, domain } = this.props; @@ -627,7 +639,7 @@ class Status extends ImmutablePureComponent { */}
- + {ancestors && (
{ancestors}
)} @@ -678,7 +690,7 @@ class Status extends ImmutablePureComponent { {descendants && (
{descendants}
)} -
+
); From 3ad3b5c84a72f39bbc382aca0ec8f8ff1473e296 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 4 Nov 2021 13:16:28 -0500 Subject: [PATCH 2/6] Status: implement proper Pull to Refresh, refactor horrible status actions --- app/soapbox/actions/statuses.js | 177 +++++------------------- app/soapbox/features/reactions/index.js | 20 ++- app/soapbox/features/reblogs/index.js | 17 ++- app/soapbox/features/status/index.js | 19 ++- 4 files changed, 68 insertions(+), 165 deletions(-) diff --git a/app/soapbox/actions/statuses.js b/app/soapbox/actions/statuses.js index eadcd59f5..5b65349b7 100644 --- a/app/soapbox/actions/statuses.js +++ b/app/soapbox/actions/statuses.js @@ -33,13 +33,9 @@ export const STATUS_HIDE = 'STATUS_HIDE'; export const REDRAFT = 'REDRAFT'; -export function fetchStatusRequest(id, skipLoading) { - return { - type: STATUS_FETCH_REQUEST, - id, - skipLoading, - }; -} +const statusExists = (getState, statusId) => { + return getState().getIn(['statuses', statusId], null) !== null; +}; export function createStatus(params, idempotencyKey) { return (dispatch, getState) => { @@ -60,43 +56,20 @@ export function createStatus(params, idempotencyKey) { export function fetchStatus(id) { return (dispatch, getState) => { - const skipLoading = getState().getIn(['statuses', id], null) !== null; + const skipLoading = statusExists(getState, id); - dispatch(fetchContext(id)); - - if (skipLoading) { - return; - } + dispatch({ type: STATUS_FETCH_REQUEST, id, skipLoading }); - dispatch(fetchStatusRequest(id, skipLoading)); - - api(getState).get(`/api/v1/statuses/${id}`).then(response => { - dispatch(importFetchedStatus(response.data)); - dispatch(fetchStatusSuccess(response.data, skipLoading)); + return api(getState).get(`/api/v1/statuses/${id}`).then(({ data: status }) => { + dispatch(importFetchedStatus(status)); + dispatch({ type: STATUS_FETCH_SUCCESS, status, skipLoading }); + return status; }).catch(error => { - dispatch(fetchStatusFail(id, error, skipLoading)); + dispatch({ type: STATUS_FETCH_FAIL, id, error, skipLoading, skipAlert: true }); }); }; } -export function fetchStatusSuccess(status, skipLoading) { - return { - type: STATUS_FETCH_SUCCESS, - status, - skipLoading, - }; -} - -export function fetchStatusFail(id, error, skipLoading) { - return { - type: STATUS_FETCH_FAIL, - id, - error, - skipLoading, - skipAlert: true, - }; -} - export function redraft(status, raw_text) { return { type: REDRAFT, @@ -115,10 +88,10 @@ export function deleteStatus(id, routerHistory, withRedraft = false) { status = status.set('poll', getState().getIn(['polls', status.get('poll')])); } - dispatch(deleteStatusRequest(id)); + dispatch({ type: STATUS_DELETE_REQUEST, id }); api(getState).delete(`/api/v1/statuses/${id}`).then(response => { - dispatch(deleteStatusSuccess(id)); + dispatch({ type: STATUS_DELETE_SUCCESS, id }); dispatch(deleteFromTimelines(id)); if (withRedraft) { @@ -126,73 +99,37 @@ export function deleteStatus(id, routerHistory, withRedraft = false) { dispatch(openModal('COMPOSE')); } }).catch(error => { - dispatch(deleteStatusFail(id, error)); + dispatch({ type: STATUS_DELETE_FAIL, id, error }); }); }; } -export function deleteStatusRequest(id) { - return { - type: STATUS_DELETE_REQUEST, - id: id, - }; -} - -export function deleteStatusSuccess(id) { - return { - type: STATUS_DELETE_SUCCESS, - id: id, - }; -} - -export function deleteStatusFail(id, error) { - return { - type: STATUS_DELETE_FAIL, - id: id, - error: error, - }; -} - export function fetchContext(id) { return (dispatch, getState) => { - dispatch(fetchContextRequest(id)); - - api(getState).get(`/api/v1/statuses/${id}/context`).then(response => { - dispatch(importFetchedStatuses(response.data.ancestors.concat(response.data.descendants))); - dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants)); - + dispatch({ type: CONTEXT_FETCH_REQUEST, id }); + + return api(getState).get(`/api/v1/statuses/${id}/context`).then(({ data: context }) => { + const { ancestors, descendants } = context; + const statuses = ancestors.concat(descendants); + dispatch(importFetchedStatuses(statuses)); + dispatch({ type: CONTEXT_FETCH_SUCCESS, id, ancestors, descendants }); + return context; }).catch(error => { if (error.response && error.response.status === 404) { dispatch(deleteFromTimelines(id)); } - dispatch(fetchContextFail(id, error)); + dispatch({ type: CONTEXT_FETCH_FAIL, id, error, skipAlert: true }); }); }; } -export function fetchContextRequest(id) { - return { - type: CONTEXT_FETCH_REQUEST, - id, - }; -} - -export function fetchContextSuccess(id, ancestors, descendants) { - return { - type: CONTEXT_FETCH_SUCCESS, - id, - ancestors, - descendants, - }; -} - -export function fetchContextFail(id, error) { - return { - type: CONTEXT_FETCH_FAIL, - id, - error, - skipAlert: true, +export function fetchStatusWithContext(id) { + return (dispatch, getState) => { + return Promise.all([ + dispatch(fetchContext(id)), + dispatch(fetchStatus(id)), + ]); }; } @@ -200,74 +137,28 @@ export function muteStatus(id) { return (dispatch, getState) => { if (!isLoggedIn(getState)) return; - dispatch(muteStatusRequest(id)); - + dispatch({ type: STATUS_MUTE_REQUEST, id }); api(getState).post(`/api/v1/statuses/${id}/mute`).then(() => { - dispatch(muteStatusSuccess(id)); + dispatch({ type: STATUS_MUTE_SUCCESS, id }); }).catch(error => { - dispatch(muteStatusFail(id, error)); + dispatch({ type: STATUS_MUTE_FAIL, id, error }); }); }; } -export function muteStatusRequest(id) { - return { - type: STATUS_MUTE_REQUEST, - id, - }; -} - -export function muteStatusSuccess(id) { - return { - type: STATUS_MUTE_SUCCESS, - id, - }; -} - -export function muteStatusFail(id, error) { - return { - type: STATUS_MUTE_FAIL, - id, - error, - }; -} - export function unmuteStatus(id) { return (dispatch, getState) => { if (!isLoggedIn(getState)) return; - dispatch(unmuteStatusRequest(id)); - + dispatch({ type: STATUS_UNMUTE_REQUEST, id }); api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => { - dispatch(unmuteStatusSuccess(id)); + dispatch({ type: STATUS_UNMUTE_SUCCESS, id }); }).catch(error => { - dispatch(unmuteStatusFail(id, error)); + dispatch({ type: STATUS_UNMUTE_FAIL, id, error }); }); }; } -export function unmuteStatusRequest(id) { - return { - type: STATUS_UNMUTE_REQUEST, - id, - }; -} - -export function unmuteStatusSuccess(id) { - return { - type: STATUS_UNMUTE_SUCCESS, - id, - }; -} - -export function unmuteStatusFail(id, error) { - return { - type: STATUS_UNMUTE_FAIL, - id, - error, - }; -} - export function hideStatus(ids) { if (!Array.isArray(ids)) { ids = [ids]; diff --git a/app/soapbox/features/reactions/index.js b/app/soapbox/features/reactions/index.js index 30de2d33a..73c4472ca 100644 --- a/app/soapbox/features/reactions/index.js +++ b/app/soapbox/features/reactions/index.js @@ -49,18 +49,24 @@ class Reactions extends ImmutablePureComponent { status: ImmutablePropTypes.map, }; + fetchData = () => { + const { dispatch, params } = this.props; + const { statusId } = params; + + dispatch(fetchFavourites(statusId)); + dispatch(fetchReactions(statusId)); + dispatch(fetchStatus(statusId)); + } + componentDidMount() { - this.props.dispatch(fetchFavourites(this.props.params.statusId)); - this.props.dispatch(fetchReactions(this.props.params.statusId)); - this.props.dispatch(fetchStatus(this.props.params.statusId)); + this.fetchData(); } componentDidUpdate(prevProps) { const { params } = this.props; - if (params.statusId !== prevProps.params.statusId && params.statusId) { - this.props.dispatch(fetchFavourites(this.props.params.statusId)); - prevProps.dispatch(fetchReactions(params.statusId)); - prevProps.dispatch(fetchStatus(params.statusId)); + + if (params.statusId !== prevProps.params.statusId) { + this.fetchData(); } } diff --git a/app/soapbox/features/reblogs/index.js b/app/soapbox/features/reblogs/index.js index 52e54017c..ea536940c 100644 --- a/app/soapbox/features/reblogs/index.js +++ b/app/soapbox/features/reblogs/index.js @@ -42,16 +42,23 @@ class Reblogs extends ImmutablePureComponent { status: ImmutablePropTypes.map, }; + fetchData = () => { + const { dispatch, params } = this.props; + const { statusId } = params; + + dispatch(fetchReblogs(statusId)); + dispatch(fetchStatus(statusId)); + } + componentDidMount() { - this.props.dispatch(fetchReblogs(this.props.params.statusId)); - this.props.dispatch(fetchStatus(this.props.params.statusId)); + this.fetchData(); } componentDidUpdate(prevProps) { const { params } = this.props; - if (params.statusId !== prevProps.params.statusId && params.statusId) { - prevProps.dispatch(fetchReblogs(params.statusId)); - prevProps.dispatch(fetchStatus(params.statusId)); + + if (params.statusId !== prevProps.params.statusId) { + this.fetchData(); } } diff --git a/app/soapbox/features/status/index.js b/app/soapbox/features/status/index.js index e56ab3d37..fbb17bae6 100644 --- a/app/soapbox/features/status/index.js +++ b/app/soapbox/features/status/index.js @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import { fetchStatus } from '../../actions/statuses'; +import { fetchStatusWithContext } from '../../actions/statuses'; import MissingIndicator from '../../components/missing_indicator'; import DetailedStatus from './components/detailed_status'; import ActionBar from './components/action_bar'; @@ -167,13 +167,15 @@ class Status extends ImmutablePureComponent { emojiSelectorFocused: false, }; - fetchStatus = () => { + fetchData = () => { const { dispatch, params } = this.props; - return dispatch(fetchStatus(params.statusId)); + const { statusId } = params; + + return dispatch(fetchStatusWithContext(statusId)); } componentDidMount() { - this.fetchStatus(); + this.fetchData(); attachFullscreenListener(this.onFullScreenChange); } @@ -538,9 +540,9 @@ class Status extends ImmutablePureComponent { const { params, status } = this.props; const { ancestorsIds } = prevProps; - if (params.statusId !== prevProps.params.statusId && params.statusId) { + if (params.statusId !== prevProps.params.statusId) { this._scrolledIntoView = false; - this.props.dispatch(fetchStatus(params.statusId)); + this.fetchData(); } if (status && status.get('id') !== prevState.loadedStatusId) { @@ -570,10 +572,7 @@ class Status extends ImmutablePureComponent { } handleRefresh = () => { - return new Promise(resolve => { - this.fetchStatus(); - resolve(); - }); + return this.fetchData(); } render() { From 9ce3aa8d1b6de4a0d4e4789ded374345dd984c12 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 4 Nov 2021 13:30:54 -0500 Subject: [PATCH 3/6] HomeTimeline: pull to refresh --- app/soapbox/components/scrollable_list.js | 16 ++++++++++++++-- app/soapbox/features/home_timeline/index.js | 6 ++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/soapbox/components/scrollable_list.js b/app/soapbox/components/scrollable_list.js index 04ceab234..ceb554c53 100644 --- a/app/soapbox/components/scrollable_list.js +++ b/app/soapbox/components/scrollable_list.js @@ -9,6 +9,7 @@ import { throttle } from 'lodash'; import { List as ImmutableList } from 'immutable'; import LoadingIndicator from './loading_indicator'; import { getSettings } from 'soapbox/actions/settings'; +import PullToRefresh from 'soapbox/components/pull_to_refresh'; const MOUSE_IDLE_DELAY = 300; @@ -43,6 +44,7 @@ class ScrollableList extends PureComponent { placeholderComponent: PropTypes.func, placeholderCount: PropTypes.number, autoload: PropTypes.bool, + onRefresh: PropTypes.func, }; state = { @@ -274,12 +276,12 @@ class ScrollableList extends PureComponent { } renderFeed = () => { - const { children, scrollKey, isLoading, hasMore, prepend, onLoadMore, placeholderComponent: Placeholder } = this.props; + const { children, scrollKey, isLoading, hasMore, prepend, onLoadMore, onRefresh, placeholderComponent: Placeholder } = this.props; const childrenCount = React.Children.count(children); const trackScroll = true; //placeholder const loadMore = (hasMore && onLoadMore) ? : null; - return ( + const feed = (
{prepend} @@ -313,6 +315,16 @@ class ScrollableList extends PureComponent {
); + + if (onRefresh) { + return ( + + {feed} + + ); + } else { + return feed; + } } render() { diff --git a/app/soapbox/features/home_timeline/index.js b/app/soapbox/features/home_timeline/index.js index d746e9991..f65198187 100644 --- a/app/soapbox/features/home_timeline/index.js +++ b/app/soapbox/features/home_timeline/index.js @@ -93,6 +93,11 @@ class HomeTimeline extends React.PureComponent { this.setState({ done: true }); } + handleRefresh = () => { + const { dispatch } = this.props; + return dispatch(expandHomeTimeline()); + } + render() { const { intl, siteTitle, isLoading, isEmpty, features } = this.props; const { done } = this.state; @@ -108,6 +113,7 @@ class HomeTimeline extends React.PureComponent { }} />} /> From f61845b876d86f11fd9e98c9b5a2cd85f8f37fd1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 4 Nov 2021 13:41:56 -0500 Subject: [PATCH 4/6] Pull to Refresh on public timelines --- app/soapbox/features/community_timeline/index.js | 7 +++++++ app/soapbox/features/public_timeline/index.js | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/app/soapbox/features/community_timeline/index.js b/app/soapbox/features/community_timeline/index.js index 0789253fc..44ee6d45f 100644 --- a/app/soapbox/features/community_timeline/index.js +++ b/app/soapbox/features/community_timeline/index.js @@ -70,6 +70,12 @@ class CommunityTimeline extends React.PureComponent { dispatch(expandCommunityTimeline({ maxId, onlyMedia })); } + handleRefresh = () => { + const { dispatch, onlyMedia } = this.props; + return dispatch(expandCommunityTimeline({ onlyMedia })); + } + + render() { const { intl, onlyMedia, timelineId } = this.props; @@ -80,6 +86,7 @@ class CommunityTimeline extends React.PureComponent { scrollKey={`${timelineId}_timeline`} timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`} onLoadMore={this.handleLoadMore} + onRefresh={this.handleRefresh} emptyMessage={} /> diff --git a/app/soapbox/features/public_timeline/index.js b/app/soapbox/features/public_timeline/index.js index 8083cf67e..1b3b0e358 100644 --- a/app/soapbox/features/public_timeline/index.js +++ b/app/soapbox/features/public_timeline/index.js @@ -94,6 +94,11 @@ class CommunityTimeline extends React.PureComponent { dispatch(expandPublicTimeline({ maxId, onlyMedia })); } + handleRefresh = () => { + const { dispatch, onlyMedia } = this.props; + return dispatch(expandPublicTimeline({ onlyMedia })); + } + render() { const { intl, onlyMedia, timelineId, siteTitle, showExplanationBox, explanationBoxExpanded } = this.props; @@ -130,6 +135,7 @@ class CommunityTimeline extends React.PureComponent { scrollKey={`${timelineId}_timeline`} timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`} onLoadMore={this.handleLoadMore} + onRefresh={this.handleRefresh} emptyMessage={} /> From 65a2a40cb220fb80ed43c8ade916aac3fb1ed56d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 4 Nov 2021 14:07:20 -0500 Subject: [PATCH 5/6] Pull to Refresh: Notifications, Chats, Bookmarks --- app/soapbox/actions/bookmarks.js | 10 ++++++---- app/soapbox/features/bookmarks/index.js | 18 ++++++++++++++---- app/soapbox/features/chats/index.js | 13 +++++++++---- app/soapbox/features/notifications/index.js | 11 +++++++---- app/styles/components/columns.scss | 16 ++++++++++++---- 5 files changed, 48 insertions(+), 20 deletions(-) diff --git a/app/soapbox/actions/bookmarks.js b/app/soapbox/actions/bookmarks.js index 3c8eec546..cf1ca8113 100644 --- a/app/soapbox/actions/bookmarks.js +++ b/app/soapbox/actions/bookmarks.js @@ -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_FAIL = 'BOOKMARKED_STATUSES_EXPAND_FAIL'; +const noOp = () => new Promise(f => f()); + export function fetchBookmarkedStatuses() { return (dispatch, getState) => { if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) { - return; + return dispatch(noOp); } 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'); dispatch(importFetchedStatuses(response.data)); dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null)); @@ -53,12 +55,12 @@ export function expandBookmarkedStatuses() { const url = getState().getIn(['status_lists', 'bookmarks', 'next'], null); if (url === null || getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) { - return; + return dispatch(noOp); } 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'); dispatch(importFetchedStatuses(response.data)); dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null)); diff --git a/app/soapbox/features/bookmarks/index.js b/app/soapbox/features/bookmarks/index.js index 0099871f5..ec4185f96 100644 --- a/app/soapbox/features/bookmarks/index.js +++ b/app/soapbox/features/bookmarks/index.js @@ -2,7 +2,8 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; 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 ImmutablePureComponent from 'react-immutable-pure-component'; import StatusList from '../../components/status_list'; @@ -38,15 +39,22 @@ class Bookmarks extends ImmutablePureComponent { isLoading: PropTypes.bool, }; - componentDidMount() { + fetchData = () => { const { dispatch } = this.props; - dispatch(fetchBookmarkedStatuses()); + return dispatch(fetchBookmarkedStatuses()); + } + + componentDidMount() { + this.fetchData(); } handleLoadMore = debounce(() => { this.props.dispatch(expandBookmarkedStatuses()); }, 300, { leading: true }) + handleRefresh = () => { + return this.fetchData(); + } render() { const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props; @@ -55,7 +63,8 @@ class Bookmarks extends ImmutablePureComponent { const emptyMessage = ; return ( - + + { + const { dispatch } = this.props; + return dispatch(fetchChats()); + } + render() { const { intl } = this.props; @@ -55,12 +60,12 @@ class ChatIndex extends React.PureComponent { onSelected={this.handleSuggestion} /> - + } /> - +
); } diff --git a/app/soapbox/features/notifications/index.js b/app/soapbox/features/notifications/index.js index 61367bad4..58f567f7d 100644 --- a/app/soapbox/features/notifications/index.js +++ b/app/soapbox/features/notifications/index.js @@ -21,7 +21,6 @@ import TimelineQueueButtonHeader from '../../components/timeline_queue_button_h import { getSettings } from 'soapbox/actions/settings'; import PlaceholderNotification from 'soapbox/features/placeholder/components/placeholder_notification'; import SubNavigation from 'soapbox/components/sub_navigation'; -import Pullable from 'soapbox/components/pullable'; const messages = defineMessages({ title: { id: 'column.notifications', defaultMessage: 'Notifications' }, @@ -129,6 +128,11 @@ class Notifications extends React.PureComponent { this.props.dispatch(dequeueNotifications()); }; + handleRefresh = () => { + const { dispatch } = this.props; + return dispatch(expandNotifications()); + } + render() { const { intl, notifications, isLoading, hasMore, showFilterBar, totalQueuedNotificationsCount } = this.props; const emptyMessage = ; @@ -173,6 +177,7 @@ class Notifications extends React.PureComponent { placeholderComponent={PlaceholderNotification} placeholderCount={20} onLoadMore={this.handleLoadOlder} + onRefresh={this.handleRefresh} onScrollToTop={this.handleScrollToTop} onScroll={this.handleScroll} > @@ -189,9 +194,7 @@ class Notifications extends React.PureComponent { count={totalQueuedNotificationsCount} message={messages.queue} /> - - {scrollContainer} - + {scrollContainer} ); } diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index 1b82cdcfd..854d5c1af 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -934,10 +934,18 @@ } // Make MaterialStatus flush against SubNavigation -.sub-navigation ~ .slist .item-list > article:first-child .material-status__status, -.sub-navigation ~ .material-status:not(.material-status + .material-status) .material-status__status { - border-top-left-radius: 0; - border-top-right-radius: 0; +.sub-navigation ~, +.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-right-radius: 0; + } + } } // Display background for loading indicator From f42427fec3c17ad78deccd0127fc20dea66184a4 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 4 Nov 2021 14:22:54 -0500 Subject: [PATCH 6/6] Column: fix PullToRefresh border-radius --- app/styles/components/columns.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss index 854d5c1af..633fb612c 100644 --- a/app/styles/components/columns.scss +++ b/app/styles/components/columns.scss @@ -977,6 +977,12 @@ .ptr, .ptr__children { background: var(--foreground-color); + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + + @media screen and (max-width: 580px) { + border-radius: 0; + } } &--transparent {