diff --git a/Makefile b/Makefile
index 5e12971f5..3c02fee6a 100644
--- a/Makefile
+++ b/Makefile
@@ -122,6 +122,7 @@ post-install:
@sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json
@sed -i'' -e 's|auto("auto", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);|auto("auto", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_FULL_USER);|g' node_modules/react-native-navigation/android/app/src/main/java/com/reactnativenavigation/params/Orientation.java
@sed -i'' -e "s|var AndroidTextInput = requireNativeComponent('AndroidTextInput', null);|var AndroidTextInput = requireNativeComponent('CustomTextInput', null);|g" node_modules/react-native/Libraries/Components/TextInput/TextInput.js
+ @sed -i'' -e 's^getItemLayout || index <= this._highestMeasuredFrameIndex,^!getItemLayout || index <= this._highestMeasuredFrameIndex,^g' node_modules/react-native/Libraries/Lists/VirtualizedList.js
@cd ./node_modules/react-native-svg/ios && rm -rf PerformanceBezier && git clone https://github.com/adamwulf/PerformanceBezier.git
@cd ./node_modules/mattermost-redux && yarn run build
diff --git a/app/actions/views/channel.js b/app/actions/views/channel.js
index d749f4fc7..cbc52009c 100644
--- a/app/actions/views/channel.js
+++ b/app/actions/views/channel.js
@@ -8,6 +8,7 @@ import {ViewTypes} from 'app/constants';
import {UserTypes} from 'mattermost-redux/action_types';
import {
fetchMyChannelsAndMembers,
+ markChannelAsRead,
selectChannel,
leaveChannel as serviceLeaveChannel,
unfavoriteChannel
@@ -236,20 +237,27 @@ export function selectInitialChannel(teamId) {
if (lastChannelId && myMembers[lastChannelId] &&
(lastChannel.team_id === teamId || isDMVisible || isGMVisible)) {
handleSelectChannel(lastChannelId)(dispatch, getState);
+ markChannelAsRead(lastChannelId)(dispatch, getState);
return;
}
const channel = Object.values(channels).find((c) => c.team_id === teamId && c.name === General.DEFAULT_CHANNEL);
+ let channelId;
if (channel) {
- dispatch(setChannelDisplayName(''));
- handleSelectChannel(channel.id)(dispatch, getState);
+ channelId = channel.id;
} else {
// Handle case when the default channel cannot be found
// so we need to get the first available channel of the team
const channelsInTeam = Object.values(channels).filter((c) => c.team_id === teamId);
const firstChannel = channelsInTeam.length ? channelsInTeam[0].id : {id: ''};
+
+ channelId = firstChannel.id;
+ }
+
+ if (channelId) {
dispatch(setChannelDisplayName(''));
- handleSelectChannel(firstChannel.id)(dispatch, getState);
+ handleSelectChannel(channelId)(dispatch, getState);
+ markChannelAsRead(channelId)(dispatch, getState);
}
};
}
diff --git a/app/actions/views/select_team.js b/app/actions/views/select_team.js
index c0357bb5d..24685e2fe 100644
--- a/app/actions/views/select_team.js
+++ b/app/actions/views/select_team.js
@@ -28,9 +28,10 @@ export function handleTeamChange(teamId, selectChannel = true) {
if (selectChannel) {
actions.push({type: ChannelTypes.SELECT_CHANNEL, data: ''});
- const lastChannelId = state.views.team.lastChannelForTeam[teamId] || '';
+ const lastChannels = state.views.team.lastChannelForTeam[teamId] || [];
+ const lastChannelId = lastChannels[0] || '';
const currentChannelId = getCurrentChannelId(state);
- viewChannel(lastChannelId, currentChannelId)(dispatch, getState);
+ viewChannel(currentChannelId)(dispatch, getState);
markChannelAsRead(lastChannelId, currentChannelId)(dispatch, getState);
}
diff --git a/app/components/inverted_flat_list/index.js b/app/components/inverted_flat_list/index.js
deleted file mode 100644
index e487abe21..000000000
--- a/app/components/inverted_flat_list/index.js
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React, {PureComponent} from 'react';
-import PropTypes from 'prop-types';
-import {FlatList, Platform, ScrollView, StyleSheet, View} from 'react-native';
-
-import RefreshList from 'app/components/refresh_list';
-
-import VirtualList from './virtual_list';
-
-export default class InvertibleFlatList extends PureComponent {
- static propTypes = {
- horizontal: PropTypes.bool,
- inverted: PropTypes.bool,
- ListFooterComponent: PropTypes.func,
- renderItem: PropTypes.func.isRequired,
- theme: PropTypes.object.isRequired
- };
-
- static defaultProps = {
- horizontal: false,
- inverted: true
- };
-
- constructor(props) {
- super(props);
-
- this.inversionDirection = props.horizontal ? styles.horizontal : styles.vertical;
- }
-
- getMetrics = () => {
- return this.flatListRef.getMetrics();
- };
-
- recordInteraction = () => {
- this.flatListRef.recordInteraction();
- };
-
- renderFooter = () => {
- const {ListFooterComponent: footer} = this.props;
- if (!footer) {
- return null;
- }
-
- return (
-
- {footer()}
-
- );
- };
-
- renderItem = (info) => {
- return (
-
- {this.props.renderItem(info)}
-
- );
- };
-
- renderScrollComponent = (props) => {
- const {theme} = this.props;
-
- if (props.onRefresh) {
- return (
-
- }
- />
- );
- }
-
- return ;
- };
-
- scrollToEnd = (params) => {
- this.flatListRef.scrollToEnd(params);
- };
-
- scrollToIndex = (params) => {
- this.flatListRef.scrollToIndex(params);
- };
-
- scrollToItem = (params) => {
- this.flatListRef.scrollToItem(params);
- };
-
- scrollToOffset = (params) => {
- this.flatListRef.scrollToOffset(params);
- };
-
- setFlatListRef = (flatListRef) => {
- this.flatListRef = flatListRef;
- };
-
- render() {
- const {inverted, ...forwardedProps} = this.props;
-
- // If not inverted, render as an ordinary FlatList
- if (!inverted) {
- return (
-
- );
- }
-
- return (
-
-
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1
- },
- vertical: Platform.select({
- android: {
- transform: [
- {perspective: 1},
- {scaleY: -1}
- ]
- },
- ios: {
- transform: [{scaleY: -1}]
- }
- }),
- horizontal: Platform.select({
- android: {
- transform: [
- {perspective: 1},
- {scaleY: -1}
- ]
- },
- ios: {
- transform: [{scaleX: -1}]
- }
- })
-});
-
diff --git a/app/components/inverted_flat_list/virtual_list.js b/app/components/inverted_flat_list/virtual_list.js
deleted file mode 100644
index 5bb05424b..000000000
--- a/app/components/inverted_flat_list/virtual_list.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {VirtualizedList} from 'react-native';
-
-/* eslint-disable */
-
-export default class Virtualized extends VirtualizedList {
- _onScroll = (e) => {
- if (this.props.onScroll) {
- this.props.onScroll(e);
- }
- const timestamp = e.timeStamp;
- const visibleLength = this._selectLength(e.nativeEvent.layoutMeasurement);
- const contentLength = this._selectLength(e.nativeEvent.contentSize);
- const offset = this._selectOffset(e.nativeEvent.contentOffset);
- const dt = Math.max(1, timestamp - this._scrollMetrics.timestamp);
- const dOffset = offset - this._scrollMetrics.offset;
- const velocity = dOffset / dt;
- this._scrollMetrics = {contentLength, dt, offset, timestamp, velocity, visibleLength};
- const {data, getItemCount, onEndReached, onEndReachedThreshold, windowSize} = this.props;
- this._updateViewableItems(data);
- if (!data) {
- return;
- }
- const distanceFromEnd = contentLength - visibleLength - offset;
- const itemCount = getItemCount(data);
- if (this.state.last === itemCount - 1 &&
- distanceFromEnd <= onEndReachedThreshold &&
- (this._hasDataChangedSinceEndReached ||
- this._scrollMetrics.contentLength !== this._sentEndForContentLength)) {
- // Only call onEndReached once for a given dataset + content length.
- this._hasDataChangedSinceEndReached = false;
- this._sentEndForContentLength = this._scrollMetrics.contentLength;
- onEndReached({distanceFromEnd});
- }
- const {first, last} = this.state;
- if ((first > 0 && velocity < 0) || (last < itemCount - 1 && velocity > 0)) {
- const distanceToContentEdge = Math.min(
- Math.abs(this._getFrameMetricsApprox(first).offset - offset),
- Math.abs(this._getFrameMetricsApprox(last).offset - (offset + visibleLength)),
- );
- const hiPri = distanceToContentEdge < (windowSize * visibleLength / 4);
- if (hiPri) {
- // Don't worry about interactions when scrolling quickly; focus on filling content as fast
- // as possible.
- this._updateCellsToRenderBatcher.dispose({abort: true});
- this._updateCellsToRender();
- return;
- }
- }
- this._updateCellsToRenderBatcher.schedule();
- };
-}
diff --git a/app/components/post_list/new_messages_divider.js b/app/components/post_list/new_messages_divider.js
index fa27dbeb0..4725b2f28 100644
--- a/app/components/post_list/new_messages_divider.js
+++ b/app/components/post_list/new_messages_divider.js
@@ -4,7 +4,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
- StyleSheet,
View,
ViewPropTypes
} from 'react-native';
@@ -47,7 +46,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
},
line: {
flex: 1,
- height: StyleSheet.hairlineWidth,
+ height: 1,
backgroundColor: theme.newMessageSeparator
},
text: {
diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js
index dc21bfb5e..bec49293c 100644
--- a/app/components/post_list/post_list.js
+++ b/app/components/post_list/post_list.js
@@ -3,10 +3,12 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
-import {StyleSheet} from 'react-native';
+import {
+ StyleSheet,
+ FlatList
+} from 'react-native';
import ChannelIntro from 'app/components/channel_intro';
-import FlatList from 'app/components/inverted_flat_list';
import Post from 'app/components/post';
import {DATE_LINE, START_OF_NEW_MESSAGES} from 'app/selectors/post_list';
import mattermostManaged from 'app/mattermost_managed';
@@ -44,6 +46,7 @@ export default class PostList extends PureComponent {
constructor(props) {
super(props);
+ this.newMessagesIndex = -1;
this.state = {
managedConfig: {}
};
@@ -55,15 +58,27 @@ export default class PostList extends PureComponent {
componentDidMount() {
this.setManagedConfig();
+ this.scrollList();
}
componentDidUpdate(prevProps) {
- if (prevProps.channelId !== this.props.channelId && this.refs.list) {
- // When switching channels make sure we start from the bottom
- this.refs.list.scrollToOffset({y: 0, animated: false});
+ const initialPosts = !prevProps.postIds.length && prevProps.postIds !== this.props.postIds;
+ if ((prevProps.channelId !== this.props.channelId || initialPosts) && this.refs.list) {
+ this.scrollList();
}
}
+ scrollList = () => {
+ requestAnimationFrame(() => {
+ if (this.props.postIds.length && this.newMessagesIndex !== -1) {
+ this.refs.list.scrollToIndex({index: this.newMessagesIndex, viewPosition: 1, viewOffset: -10, animated: true});
+ this.newMessagesIndex = -1;
+ } else {
+ this.refs.list.scrollToOffset({y: 0, animated: false});
+ }
+ });
+ };
+
setManagedConfig = async (config) => {
let nextConfig = config;
if (!nextConfig) {
@@ -73,11 +88,7 @@ export default class PostList extends PureComponent {
this.setState({
managedConfig: nextConfig
});
- }
-
- getItem = (data, index) => data[index];
-
- getItemCount = (data) => data.length;
+ };
keyExtractor = (item) => {
// All keys are strings (either post IDs or special keys)
@@ -102,6 +113,7 @@ export default class PostList extends PureComponent {
renderItem = ({item, index}) => {
if (item === START_OF_NEW_MESSAGES) {
+ this.newMessagesIndex = index;
return (
);
diff --git a/app/screens/channel/channel.js b/app/screens/channel/channel.js
index 3da4afc82..c3932f2be 100644
--- a/app/screens/channel/channel.js
+++ b/app/screens/channel/channel.js
@@ -64,14 +64,6 @@ class Channel extends PureComponent {
}
}
- componentDidMount() {
- // Mark current channel as read when opening app while logged in
- if (this.props.currentChannelId) {
- this.props.actions.markChannelAsRead(this.props.currentChannelId);
- this.props.actions.viewChannel(this.props.currentChannelId);
- }
- }
-
componentWillReceiveProps(nextProps) {
if (nextProps.currentTeamId && this.props.currentTeamId !== nextProps.currentTeamId) {
this.loadChannels(nextProps.currentTeamId);
diff --git a/app/selectors/post_list.js b/app/selectors/post_list.js
index 2d2730a23..b21e1556d 100644
--- a/app/selectors/post_list.js
+++ b/app/selectors/post_list.js
@@ -62,8 +62,8 @@ export function makePreparePostIdsForPostList() {
}
// Only add the new messages line if a lastViewedAt time is set
- const postIsUnread = post.create_at > lastViewedAt;
- if (lastViewedAt != null && !addedNewMessagesIndicator && postIsUnread) {
+ const postIsUnread = post.create_at > lastViewedAt && post.user_id !== currentUserId;
+ if (lastViewedAt !== null && !addedNewMessagesIndicator && postIsUnread) {
out.push(START_OF_NEW_MESSAGES);
addedNewMessagesIndicator = true;
}
diff --git a/yarn.lock b/yarn.lock
index 739a3117c..1e1e993eb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3938,7 +3938,7 @@ makeerror@1.0.x:
mattermost-redux@mattermost/mattermost-redux#master:
version "1.0.1"
- resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/ae530a32d0d4c5b0f3c8200f7d7890439e6cf9ca"
+ resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/3c8c5c315e44daec2cbe0d6d88a79555e011c8d4"
dependencies:
deep-equal "1.0.1"
form-data "2.3.1"