Bring back refresh control for channels and threads (#5511) (#5520)

(cherry picked from commit 073b836beb)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Mattermost Build 2021-07-07 19:04:07 +02:00 committed by GitHub
parent 456ab53ecf
commit 5b5b7bc620
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 63 additions and 37 deletions

View file

@ -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)));
};
}

View file

@ -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,

View file

@ -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<ActionResult>;
handleSelectChannelByName: (channelName: string, teamName: string, errorHandler: (intl: typeof intlShape) => void, intl: typeof intlShape) => Promise<ActionResult>;
highlightPinnedOrFlagged?: boolean;
highlightPostId?: string;
@ -43,7 +44,9 @@ type PostListProps = {
location: string;
onLoadMoreUp: () => void;
postIds: string[];
refreshChannelWithRetry: (channelId: string) => Promise<ActionResult>;
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<FlatList<never>>(null);
const onScrollEndIndexListener = useRef<onScrollEndIndexListenerEvent>();
const onViewableItemsChangedListener = useRef<ViewableItemsChangedListenerEvent>();
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 = (
<RefreshControl
refreshing={false}
enabled={Platform.OS === 'ios'}
colors={[theme.centerChannelColor]}
tintColor={theme.centerChannelColor}
/>);
}
const refreshControl = useMemo(() => (
<RefreshControl
refreshing={refreshing}
colors={[theme.onlineIndicator, theme.awayIndicator, theme.dndIndicator]}
tintColor={theme.centerChannelColor}
onRefresh={onRefresh}
/>
), [refreshing, theme]);
return (
<>

View file

@ -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,

View file

@ -15,7 +15,6 @@ describe('ChannelPostList', () => {
getPostThread: jest.fn(),
increasePostVisibility: jest.fn(),
selectPost: jest.fn(),
refreshChannelWithRetry: jest.fn(),
},
channelId: 'channel-id',
loadMorePostsVisible: false,

View file

@ -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),
};
}

View file

@ -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"
/>

View file

@ -42,6 +42,7 @@ export default class ThreadAndroid extends ThreadBase {
currentUserId={myMember && myMember.user_id}
lastViewedAt={this.state.lastViewedAt}
location={THREAD}
rootId={rootId}
/>
</Animated.View>
<PostDraft

View file

@ -55,6 +55,7 @@ export default class ThreadIOS extends ThreadBase {
currentUserId={myMember && myMember.user_id}
lastViewedAt={this.state.lastViewedAt}
location={THREAD}
rootId={rootId}
scrollViewNativeID={SCROLLVIEW_NATIVE_ID}
/>
</Animated.View>

View file

@ -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<Props, State> {
// 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]},
<ScrollViewClass
{...props}
style={[baseStyle, inner]}
diff --git a/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
index dffccc4..dc426c2 100644
--- a/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
@ -40,10 +61,10 @@ index 2249d54..cc303b3 100644
horizontallyInverted: {
transform: [{scaleX: -1}],
diff --git a/node_modules/react-native/react.gradle b/node_modules/react-native/react.gradle
index 5995ad5..dbae7e3 100644
index dd34c98..4d74938 100644
--- a/node_modules/react-native/react.gradle
+++ b/node_modules/react-native/react.gradle
@@ -157,7 +157,7 @@ afterEvaluate {
@@ -151,7 +151,7 @@ afterEvaluate {
// Set up dev mode
def devEnabled = !(config."devDisabledIn${targetName}"
@ -52,7 +73,7 @@ index 5995ad5..dbae7e3 100644
def extraArgs = config.extraPackagerArgs ?: [];
@@ -177,7 +177,7 @@ afterEvaluate {
@@ -171,7 +171,7 @@ afterEvaluate {
def hermesFlags;
def hbcTempFile = file("${jsBundleFile}.hbc")
exec {
@ -61,7 +82,7 @@ index 5995ad5..dbae7e3 100644
// Can't use ?: since that will also substitute valid empty lists
hermesFlags = config.hermesFlagsRelease
if (hermesFlags == null) hermesFlags = ["-O", "-output-source-map"]
@@ -221,7 +221,7 @@ afterEvaluate {
@@ -215,7 +215,7 @@ afterEvaluate {
? config."bundleIn${targetName}"
: config."bundleIn${variant.buildType.name.capitalize()}" != null
? config."bundleIn${variant.buildType.name.capitalize()}"
@ -70,7 +91,7 @@ index 5995ad5..dbae7e3 100644
}
// Expose a minimal interface on the application variant and the task itself:
@@ -318,7 +318,7 @@ afterEvaluate {
@@ -312,7 +312,7 @@ afterEvaluate {
// This should really be done by packaging all Hermes releated libs into
// two separate HermesDebug and HermesRelease AARs, but until then we'll
// kludge it by deleting the .so files out of the /transforms/ directory.