From 5b5b7bc620850971c6f44c3ea64bb3ed35e1948b Mon Sep 17 00:00:00 2001 From: Mattermost Build Date: Wed, 7 Jul 2021 19:04:07 +0200 Subject: [PATCH] Bring back refresh control for channels and threads (#5511) (#5520) (cherry picked from commit 073b836beb658748530c537478c46189ba4357d9) Co-authored-by: Elias Nahum --- app/actions/views/channel.js | 11 +---- app/components/post_list/index.ts | 2 + app/components/post_list/post_list.tsx | 42 ++++++++++++------- .../channel_post_list/channel_post_list.js | 1 - .../channel_post_list.test.js | 1 - .../channel/channel_post_list/index.js | 7 +--- .../__snapshots__/thread.ios.test.js.snap | 3 ++ app/screens/thread/thread.android.js | 1 + app/screens/thread/thread.ios.js | 1 + patches/react-native+0.64.2.patch | 31 +++++++++++--- 10 files changed, 63 insertions(+), 37 deletions(-) diff --git a/app/actions/views/channel.js b/app/actions/views/channel.js index efaee5c3d..dfc987ce4 100644 --- a/app/actions/views/channel.js +++ b/app/actions/views/channel.js @@ -503,16 +503,7 @@ export function closeGMChannel(channel) { export function refreshChannelWithRetry(channelId) { return async (dispatch) => { - dispatch(setChannelRefreshing(true)); - const posts = await dispatch(fetchPostActionWithRetry(getPosts(channelId))); - const actions = [setChannelRefreshing(false)]; - - if (posts) { - actions.push(setChannelRetryFailed(false)); - } - - dispatch(batchActions(actions, 'BATCH_REEFRESH_CHANNEL')); - return posts; + return dispatch(fetchPostActionWithRetry(getPosts(channelId))); }; } diff --git a/app/components/post_list/index.ts b/app/components/post_list/index.ts index f1326995d..0e09ae430 100644 --- a/app/components/post_list/index.ts +++ b/app/components/post_list/index.ts @@ -5,6 +5,7 @@ import {connect} from 'react-redux'; import {handleSelectChannelByName, refreshChannelWithRetry} from '@actions/views/channel'; import {closePermalink, showPermalink} from '@actions/views/permalink'; +import {getPostThread} from '@actions/views/post'; import {setDeepLinkURL} from '@actions/views/root'; import {getConfig, getCurrentUrl} from '@mm-redux/selectors/entities/general'; import {getTheme} from '@mm-redux/selectors/entities/preferences'; @@ -48,6 +49,7 @@ function mapStateToProps() { const mapDispatchToProps = { closePermalink, + getPostThread, handleSelectChannelByName, refreshChannelWithRetry, setDeepLinkURL, diff --git a/app/components/post_list/post_list.tsx b/app/components/post_list/post_list.tsx index e00486490..07c072100 100644 --- a/app/components/post_list/post_list.tsx +++ b/app/components/post_list/post_list.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {ReactElement, useCallback, useEffect, useLayoutEffect, useRef} from 'react'; +import React, {ReactElement, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; import {injectIntl, intlShape} from 'react-intl'; import {DeviceEventEmitter, FlatList, Platform, RefreshControl, StyleSheet, ViewToken} from 'react-native'; @@ -34,6 +34,7 @@ type PostListProps = { currentTeamName: string; deepLinkURL?: string; extraData: never; + getPostThread: (rootId: string) => Promise; handleSelectChannelByName: (channelName: string, teamName: string, errorHandler: (intl: typeof intlShape) => void, intl: typeof intlShape) => Promise; highlightPinnedOrFlagged?: boolean; highlightPostId?: string; @@ -43,7 +44,9 @@ type PostListProps = { location: string; onLoadMoreUp: () => void; postIds: string[]; + refreshChannelWithRetry: (channelId: string) => Promise; renderFooter: () => ReactElement | null; + rootId?: string; scrollViewNativeID?: string; serverURL: string; shouldRenderReplyButton?: boolean; @@ -81,8 +84,9 @@ const styles = StyleSheet.create({ const buildExtraData = makeExtraData(); const PostList = ({ - channelId, currentTeamName = '', closePermalink, deepLinkURL, extraData, handleSelectChannelByName, highlightPostId, highlightPinnedOrFlagged, initialIndex, intl, - loadMorePostsVisible, location, onLoadMoreUp = emptyFunction, postIds = [], renderFooter = (() => null), + channelId, currentTeamName = '', closePermalink, deepLinkURL, extraData, getPostThread, + handleSelectChannelByName, highlightPostId, highlightPinnedOrFlagged, initialIndex, intl, loadMorePostsVisible, + location, onLoadMoreUp = emptyFunction, postIds = [], refreshChannelWithRetry, renderFooter = (() => null), rootId, serverURL = '', setDeepLinkURL, showMoreMessagesButton, showPermalink, siteURL = '', scrollViewNativeID, shouldRenderReplyButton, testID, theme, }: PostListProps) => { const prevChannelId = useRef(channelId); @@ -90,6 +94,7 @@ const PostList = ({ const flatListRef = useRef>(null); const onScrollEndIndexListener = useRef(); const onViewableItemsChangedListener = useRef(); + const [refreshing, setRefreshing] = useState(false); const registerViewableItemsListener = useCallback((listener) => { onViewableItemsChangedListener.current = listener; @@ -118,6 +123,18 @@ const PostList = ({ showPermalink(intl, teamName, postId); }; + const onRefresh = async () => { + if (location === Screens.CHANNEL && channelId) { + setRefreshing(true); + await refreshChannelWithRetry(channelId); + setRefreshing(false); + } else if (location === Screens.THREAD && rootId) { + setRefreshing(true); + await getPostThread(rootId); + setRefreshing(false); + } + }; + const onScrollToIndexFailed = useCallback((info: ScrollIndexFailed) => { const animationFrameIndexFailed = requestAnimationFrame(() => { const index = Math.min(info.highestMeasuredFrameIndex, info.index); @@ -283,17 +300,14 @@ const PostList = ({ } }, [initialIndex, highlightPostId]); - let refreshControl; - if (location === Screens.PERMALINK) { - // Hack so that the scrolling the permalink does not dismisses the modal screens - refreshControl = ( - ); - } + const refreshControl = useMemo(() => ( + + ), [refreshing, theme]); return ( <> diff --git a/app/screens/channel/channel_post_list/channel_post_list.js b/app/screens/channel/channel_post_list/channel_post_list.js index 250dfdc48..e2d264b19 100644 --- a/app/screens/channel/channel_post_list/channel_post_list.js +++ b/app/screens/channel/channel_post_list/channel_post_list.js @@ -23,7 +23,6 @@ export default class ChannelPostList extends PureComponent { getPostThread: PropTypes.func.isRequired, increasePostVisibility: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, - refreshChannelWithRetry: PropTypes.func.isRequired, setChannelRefreshing: PropTypes.func, }).isRequired, channelId: PropTypes.string.isRequired, diff --git a/app/screens/channel/channel_post_list/channel_post_list.test.js b/app/screens/channel/channel_post_list/channel_post_list.test.js index 8d5f44445..65b23a441 100644 --- a/app/screens/channel/channel_post_list/channel_post_list.test.js +++ b/app/screens/channel/channel_post_list/channel_post_list.test.js @@ -15,7 +15,6 @@ describe('ChannelPostList', () => { getPostThread: jest.fn(), increasePostVisibility: jest.fn(), selectPost: jest.fn(), - refreshChannelWithRetry: jest.fn(), }, channelId: 'channel-id', loadMorePostsVisible: false, diff --git a/app/screens/channel/channel_post_list/index.js b/app/screens/channel/channel_post_list/index.js index 0421adb25..756f3f446 100644 --- a/app/screens/channel/channel_post_list/index.js +++ b/app/screens/channel/channel_post_list/index.js @@ -4,11 +4,7 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import { - loadPostsIfNecessaryWithRetry, - increasePostVisibility, - refreshChannelWithRetry, -} from '@actions/views/channel'; +import {loadPostsIfNecessaryWithRetry, increasePostVisibility} from '@actions/views/channel'; import {getPostThread} from '@actions/views/post'; import {Types} from '@constants'; import {selectPost} from '@mm-redux/actions/posts'; @@ -45,7 +41,6 @@ function mapDispatchToProps(dispatch) { getPostThread, increasePostVisibility, selectPost, - refreshChannelWithRetry, }, dispatch), }; } diff --git a/app/screens/thread/__snapshots__/thread.ios.test.js.snap b/app/screens/thread/__snapshots__/thread.ios.test.js.snap index 21abf20c5..3d3c3caf7 100644 --- a/app/screens/thread/__snapshots__/thread.ios.test.js.snap +++ b/app/screens/thread/__snapshots__/thread.ios.test.js.snap @@ -44,6 +44,7 @@ exports[`thread should match snapshot, has root post 1`] = ` style={Object {}} /> } + rootId="root_id" scrollViewNativeID="threadPostList" testID="thread.post_list" /> @@ -134,6 +135,7 @@ exports[`thread should match snapshot, render footer 1`] = ` style={Object {}} /> } + rootId="root_id" scrollViewNativeID="threadPostList" testID="thread.post_list" /> @@ -153,6 +155,7 @@ exports[`thread should match snapshot, render footer 2`] = ` ] } renderFooter={null} + rootId="root_id" scrollViewNativeID="threadPostList" testID="thread.post_list" /> diff --git a/app/screens/thread/thread.android.js b/app/screens/thread/thread.android.js index 429564459..d2cb8c526 100644 --- a/app/screens/thread/thread.android.js +++ b/app/screens/thread/thread.android.js @@ -42,6 +42,7 @@ export default class ThreadAndroid extends ThreadBase { currentUserId={myMember && myMember.user_id} lastViewedAt={this.state.lastViewedAt} location={THREAD} + rootId={rootId} /> diff --git a/patches/react-native+0.64.2.patch b/patches/react-native+0.64.2.patch index 7b844451b..bb2e25a7a 100644 --- a/patches/react-native+0.64.2.patch +++ b/patches/react-native+0.64.2.patch @@ -1,3 +1,24 @@ +diff --git a/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.js b/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.js +index a069a24..a21fa38 100644 +--- a/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.js ++++ b/node_modules/react-native/Libraries/Components/ScrollView/ScrollView.js +@@ -1222,9 +1222,15 @@ class ScrollView extends React.Component { + // Note: we should split props.style on the inner and outer props + // however, the ScrollView still needs the baseStyle to be scrollable + const {outer, inner} = splitLayoutProps(flattenStyle(props.style)); ++ let inverted; ++ if (inner.scaleY) { ++ inverted = {scaleY: -1}; ++ delete inner['scaleY'] ++ } ++ + return React.cloneElement( + refreshControl, +- {style: [baseStyle, outer]}, ++ {style: [baseStyle, outer, inverted]}, +