MM-14958 Scroll to bottom when posting a new message (#2761)

This commit is contained in:
Elias Nahum 2019-05-07 03:58:16 -04:00 committed by Sudheer
parent 963adcdc9f
commit 5ce9208d32
11 changed files with 196 additions and 360 deletions

View file

@ -725,7 +725,7 @@ Common code (API client, Redux stores, logic, utility functions) for building a
* LICENSE: Apache-2.0
Copyright 2015-present Mattermost, Inc.
Copyright 2015-present Mattermost, Inc.
Apache License
Version 2.0, January 2004
@ -1401,7 +1401,7 @@ SOFTWARE.
This product contains 'react-native-doc-viewer' by Philipp Hecht.
React Native Native Module Bridge Quicklock Document Viewer for IOS + Android supports pdf, png, jpg, xls, ppt, doc, docx, pptx, xlx + Video Player mp4 supported
React Native Native Module Bridge Quicklock Document Viewer for IOS + Android supports pdf, png, jpg, xls, ppt, doc, docx, pptx, xlx + Video Player mp4 supported
* HOMEPAGE:
* https://github.com/philipphecht/react-native-doc-viewer/blob/master/README.md
@ -1859,41 +1859,6 @@ SOFTWARE.
---
## react-native-recyclerview-list
This product contains 'react-native-recyclerview-list' by GitHub user "godness84".
A RecyclerView implementation for React Native
* HOMEPAGE:
* https://github.com/godness84/react-native-recyclerview-list#readme
* LICENSE: MIT
The MIT License (MIT)
Copyright (c) 2015 Marc Shilling
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
## react-native-safe-area
This product contains 'react-native-safe-area' by Masayuki Iwai.
@ -1942,16 +1907,16 @@ This package simplifies constructing the getItemLayout prop for react native Sec
Copyright (c) 2017 Jan Soendermann
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---

View file

@ -229,7 +229,6 @@ dependencies {
implementation project(':react-native-sentry')
implementation project(':react-native-exception-handler')
implementation project(':rn-fetch-blob')
implementation project(':react-native-recyclerview-list')
implementation project(':react-native-webview')
implementation project(':react-native-gesture-handler')

View file

@ -22,7 +22,6 @@ import com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage;
import com.RNFetchBlob.RNFetchBlobPackage;
import com.gantix.JailMonkey.JailMonkeyPackage;
import io.tradle.react.LocalAuthPackage;
import com.github.godness84.RNRecyclerViewList.RNRecyclerviewListPackage;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
@ -84,7 +83,6 @@ public class MainApplication extends NavigationApplication implements INotificat
new SharePackage(this),
new KeychainPackage(),
new InitializationPackage(this),
new RNRecyclerviewListPackage(),
new RNCWebViewPackage(),
new RNGestureHandlerPackage()
);

View file

@ -41,7 +41,5 @@ include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-linear-gradient'
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
include ':react-native-recyclerview-list'
project(':react-native-recyclerview-list').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-recyclerview-list/android')
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')

View file

@ -1,125 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {StyleSheet} from 'react-native';
import RecyclerViewList, {DataSource, RecyclerRefreshControl} from 'react-native-recyclerview-list';
import {ListTypes} from 'app/constants';
import PostListBase from './post_list_base';
const SCROLL_UP_MULTIPLIER = 3.5;
export default class PostList extends PostListBase {
constructor(props) {
super(props);
this.contentOffsetY = 0;
this.state = {
refreshing: false,
dataSource: new DataSource(props.postIds, this.keyExtractor),
};
}
componentWillReceiveProps(nextProps) {
if (this.props.channelId !== nextProps.channelId) {
this.contentOffsetY = 0;
this.contentHeight = 0;
}
if (nextProps.postIds !== this.props.postIds) {
this.setState({
dataSource: new DataSource(nextProps.postIds, this.keyExtractor),
});
}
}
handleLayout = (layoutHeight, contentWidth, contentHeight) => {
if (layoutHeight && contentHeight < layoutHeight) {
// We still have less than 1 screen of posts loaded with more to get, so load more
this.props.onLoadMoreUp();
}
};
handleScroll = (event) => {
const {layoutMeasurement, contentOffset, contentSize} = event.nativeEvent;
if (!this.postListHeight) {
this.handleLayout(layoutMeasurement.height, contentSize.width, contentSize.height);
}
this.postListHeight = layoutMeasurement.height;
if (contentOffset.y >= 0) {
const definedHeight = Math.round(this.postListHeight) * SCROLL_UP_MULTIPLIER;
const pageOffsetY = contentOffset.y;
const direction = (this.contentOffsetY < pageOffsetY) ?
ListTypes.VISIBILITY_SCROLL_DOWN :
ListTypes.VISIBILITY_SCROLL_UP;
this.contentOffsetY = contentOffset.y;
switch (direction) {
case ListTypes.VISIBILITY_SCROLL_UP:
if (Math.round(pageOffsetY) < definedHeight) {
this.props.onLoadMoreUp();
}
break;
}
}
};
render() {
const {
channelId,
initialIndex,
postIds,
} = this.props;
const otherProps = {};
const footer = typeof this.props.renderFooter === 'object' ? this.props.renderFooter : this.props.renderFooter();
if (postIds.length) {
otherProps.ListFooterComponent = footer;
} else {
otherProps.ListEmptyComponent = footer;
}
const hasPostsKey = postIds.length ? 'true' : 'false';
return (
<RecyclerRefreshControl
key={`recyclerFor-${channelId}-${hasPostsKey}`}
colors={['red', 'blue']}
direction='bottom'
enabled={Boolean(channelId)}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
style={styles.flex}
>
<RecyclerViewList
key={`${channelId}-${hasPostsKey}`}
ref={'list'}
style={[styles.flex, styles.postListContent]}
dataSource={this.state.dataSource}
renderItem={this.renderItem}
initialScrollIndex={initialIndex}
initialScrollPosition={RecyclerViewList.Constants.ScrollPosition.CENTER}
inverted={true}
onScroll={this.handleScroll}
onContentSizeChange={this.onContentSizeChange}
windowSize={60}
{...otherProps}
/>
</RecyclerRefreshControl>
);
}
}
const styles = StyleSheet.create({
flex: {
flex: 1,
},
postListContent: {
paddingTop: 5,
},
});

View file

@ -1,146 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {FlatList, StyleSheet} from 'react-native';
import {ListTypes} from 'app/constants';
import {makeExtraData} from 'app/utils/list_view';
import PostListBase from './post_list_base';
const INITIAL_BATCH_TO_RENDER = 15;
const SCROLL_UP_MULTIPLIER = 3.5;
const SCROLL_POSITION_CONFIG = {
// To avoid scrolling the list when new messages arrives
// if the user is not at the bottom
minIndexForVisible: 0,
// If the user is at the bottom or 60px from the bottom
// auto scroll show the new message
autoscrollToTopThreshold: 60,
};
export default class PostList extends PostListBase {
constructor(props) {
super(props);
this.hasDoneInitialScroll = false;
this.contentOffsetY = 0;
this.makeExtraData = makeExtraData();
this.state = {
postListHeight: 0,
};
}
componentWillReceiveProps(nextProps) {
if (this.props.channelId !== nextProps.channelId) {
this.contentOffsetY = 0;
this.hasDoneInitialScroll = false;
}
}
handleContentSizeChange = (contentWidth, contentHeight) => {
this.scrollToInitialIndexIfNeeded(contentWidth, contentHeight);
if (this.state.postListHeight && contentHeight < this.state.postListHeight && this.props.extraData) {
// We still have less than 1 screen of posts loaded with more to get, so load more
this.props.onLoadMoreUp();
}
};
handleLayout = (event) => {
const {height} = event.nativeEvent.layout;
this.setState({postListHeight: height});
};
handleScroll = (event) => {
const pageOffsetY = event.nativeEvent.contentOffset.y;
if (pageOffsetY > 0) {
const contentHeight = event.nativeEvent.contentSize.height;
const direction = (this.contentOffsetY < pageOffsetY) ?
ListTypes.VISIBILITY_SCROLL_UP :
ListTypes.VISIBILITY_SCROLL_DOWN;
this.contentOffsetY = pageOffsetY;
if (
direction === ListTypes.VISIBILITY_SCROLL_UP &&
(contentHeight - pageOffsetY) < (this.state.postListHeight * SCROLL_UP_MULTIPLIER)
) {
this.props.onLoadMoreUp();
}
}
};
handleScrollToIndexFailed = () => {
requestAnimationFrame(() => {
this.hasDoneInitialScroll = false;
this.scrollToInitialIndexIfNeeded(1, 1);
});
};
scrollToInitialIndexIfNeeded = (width, height) => {
if (
width > 0 &&
height > 0 &&
this.props.initialIndex > 0 &&
!this.hasDoneInitialScroll
) {
this.refs.list.scrollToIndex({
animated: false,
index: this.props.initialIndex,
viewPosition: 0.5,
});
this.hasDoneInitialScroll = true;
}
};
render() {
const {
channelId,
highlightPostId,
postIds,
refreshing,
} = this.props;
const refreshControl = {refreshing};
if (channelId) {
refreshControl.onRefresh = this.handleRefresh;
}
const hasPostsKey = postIds.length ? 'true' : 'false';
return (
<FlatList
key={`recyclerFor-${channelId}-${hasPostsKey}`}
ref='list'
contentContainerStyle={styles.postListContent}
data={postIds}
extraData={this.makeExtraData(channelId, highlightPostId, this.props.extraData)}
initialNumToRender={INITIAL_BATCH_TO_RENDER}
inverted={true}
keyExtractor={this.keyExtractor}
ListFooterComponent={this.props.renderFooter}
maintainVisibleContentPosition={SCROLL_POSITION_CONFIG}
maxToRenderPerBatch={INITIAL_BATCH_TO_RENDER + 1}
onContentSizeChange={this.handleContentSizeChange}
onLayout={this.handleLayout}
onScroll={this.handleScroll}
onScrollToIndexFailed={this.handleScrollToIndexFailed}
removeClippedSubviews={true}
renderItem={this.renderItem}
scrollEventThrottle={60}
{...refreshControl}
/>
);
}
}
const styles = StyleSheet.create({
postListContent: {
paddingTop: 5,
},
});

View file

@ -3,20 +3,36 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {FlatList, StyleSheet} from 'react-native';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import * as PostListUtils from 'mattermost-redux/utils/post_list';
import CombinedUserActivityPost from 'app/components/combined_user_activity_post';
import Post from 'app/components/post';
import {DeepLinkTypes} from 'app/constants';
import {DeepLinkTypes, ListTypes} from 'app/constants';
import mattermostManaged from 'app/mattermost_managed';
import {makeExtraData} from 'app/utils/list_view';
import {changeOpacity} from 'app/utils/theme';
import {matchDeepLink} from 'app/utils/url';
import DateHeader from './date_header';
import NewMessagesDivider from './new_messages_divider';
export default class PostListBase extends PureComponent {
const INITIAL_BATCH_TO_RENDER = 15;
const SCROLL_UP_MULTIPLIER = 3.5;
const SCROLL_POSITION_CONFIG = {
// To avoid scrolling the list when new messages arrives
// if the user is not at the bottom
minIndexForVisible: 0,
// If the user is at the bottom or 60px from the bottom
// auto scroll show the new message
autoscrollToTopThreshold: 60,
};
export default class PostList extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
handleSelectChannelByName: PropTypes.func.isRequired,
@ -58,6 +74,30 @@ export default class PostListBase extends PureComponent {
siteURL: '',
};
constructor(props) {
super(props);
this.hasDoneInitialScroll = false;
this.contentOffsetY = 0;
this.makeExtraData = makeExtraData();
this.flatListRef = React.createRef();
this.state = {
postListHeight: 0,
};
}
componentDidMount() {
EventEmitter.on('scroll-to-bottom', this.scrollToBottom);
}
componentWillReceiveProps(nextProps) {
if (this.props.channelId !== nextProps.channelId) {
this.contentOffsetY = 0;
this.hasDoneInitialScroll = false;
}
}
componentDidUpdate() {
if (this.props.deepLinkURL) {
this.handleDeepLink(this.props.deepLinkURL);
@ -65,12 +105,25 @@ export default class PostListBase extends PureComponent {
}
}
componentWillUnmount() {
EventEmitter.off('scroll-to-bottom', this.scrollToBottom);
}
handleClosePermalink = () => {
const {actions} = this.props;
actions.selectFocusedPostId('');
this.showingPermalink = false;
};
handleContentSizeChange = (contentWidth, contentHeight) => {
this.scrollToInitialIndexIfNeeded(contentWidth, contentHeight);
if (this.state.postListHeight && contentHeight < this.state.postListHeight && this.props.extraData) {
// We still have less than 1 screen of posts loaded with more to get, so load more
this.props.onLoadMoreUp();
}
};
handleDeepLink = (url) => {
const {serverURL, siteURL} = this.props;
@ -84,6 +137,22 @@ export default class PostListBase extends PureComponent {
}
};
handleLayout = (event) => {
const {height} = event.nativeEvent.layout;
this.setState({postListHeight: height});
};
handlePermalinkPress = (postId, teamName) => {
const {actions, onPermalinkPress} = this.props;
if (onPermalinkPress) {
onPermalinkPress(postId, true);
} else {
actions.loadChannelsByTeamName(teamName);
this.showPermalinkView(postId);
}
};
handleRefresh = () => {
const {
actions,
@ -100,17 +169,31 @@ export default class PostListBase extends PureComponent {
}
};
handlePermalinkPress = (postId, teamName) => {
const {actions, onPermalinkPress} = this.props;
handleScroll = (event) => {
const pageOffsetY = event.nativeEvent.contentOffset.y;
if (pageOffsetY > 0) {
const contentHeight = event.nativeEvent.contentSize.height;
const direction = (this.contentOffsetY < pageOffsetY) ?
ListTypes.VISIBILITY_SCROLL_UP :
ListTypes.VISIBILITY_SCROLL_DOWN;
if (onPermalinkPress) {
onPermalinkPress(postId, true);
} else {
actions.loadChannelsByTeamName(teamName);
this.showPermalinkView(postId);
this.contentOffsetY = pageOffsetY;
if (
direction === ListTypes.VISIBILITY_SCROLL_UP &&
(contentHeight - pageOffsetY) < (this.state.postListHeight * SCROLL_UP_MULTIPLIER)
) {
this.props.onLoadMoreUp();
}
}
};
handleScrollToIndexFailed = () => {
requestAnimationFrame(() => {
this.hasDoneInitialScroll = false;
this.scrollToInitialIndexIfNeeded(1, 1);
});
};
keyExtractor = (item) => {
// All keys are strings (either post IDs or special keys)
return item;
@ -178,6 +261,27 @@ export default class PostListBase extends PureComponent {
);
};
scrollToBottom = () => {
this.flatListRef.current.scrollToOffset({offset: 0, animated: true});
};
scrollToInitialIndexIfNeeded = (width, height) => {
if (
width > 0 &&
height > 0 &&
this.props.initialIndex > 0 &&
!this.hasDoneInitialScroll
) {
this.flatListRef.current.scrollToIndex({
animated: false,
index: this.props.initialIndex,
viewOffset: 50,
viewPosition: 0.5,
});
this.hasDoneInitialScroll = true;
}
};
showPermalinkView = (postId) => {
const {actions, navigator} = this.props;
@ -204,5 +308,51 @@ export default class PostListBase extends PureComponent {
navigator.showModal(options);
}
};
render() {
const {
channelId,
highlightPostId,
postIds,
refreshing,
} = this.props;
const refreshControl = {refreshing};
if (channelId) {
refreshControl.onRefresh = this.handleRefresh;
}
const hasPostsKey = postIds.length ? 'true' : 'false';
return (
<FlatList
key={`recyclerFor-${channelId}-${hasPostsKey}`}
ref={this.flatListRef}
contentContainerStyle={styles.postListContent}
data={postIds}
extraData={this.makeExtraData(channelId, highlightPostId, this.props.extraData)}
initialNumToRender={INITIAL_BATCH_TO_RENDER}
inverted={true}
keyExtractor={this.keyExtractor}
ListFooterComponent={this.props.renderFooter}
maintainVisibleContentPosition={SCROLL_POSITION_CONFIG}
maxToRenderPerBatch={INITIAL_BATCH_TO_RENDER + 1}
onContentSizeChange={this.handleContentSizeChange}
onLayout={this.handleLayout}
onScroll={this.handleScroll}
onScrollToIndexFailed={this.handleScrollToIndexFailed}
removeClippedSubviews={true}
renderItem={this.renderItem}
scrollEventThrottle={60}
{...refreshControl}
/>
);
}
}
const styles = StyleSheet.create({
postListContent: {
paddingTop: 5,
},
});

View file

@ -4,7 +4,7 @@
import React from 'react';
import {shallow} from 'enzyme';
import PostList from './post_list.ios.js';
import PostList from './post_list';
import Preferences from 'mattermost-redux/constants/preferences';
jest.useFakeTimers();

View file

@ -383,6 +383,8 @@ export default class PostTextbox extends PureComponent {
this.setState(nextState, callback);
}
EventEmitter.emit('scroll-to-bottom');
}
};

56
package-lock.json generated
View file

@ -1974,7 +1974,7 @@
},
"array-equal": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
"dev": true
},
@ -3116,7 +3116,7 @@
"dependencies": {
"core-js": {
"version": "1.2.7",
"resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
},
"fbjs": {
@ -3147,7 +3147,7 @@
},
"css-select": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"dev": true,
"requires": {
@ -4168,7 +4168,7 @@
},
"external-editor": {
"version": "2.2.0",
"resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
"integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
"requires": {
"chardet": "^0.4.0",
@ -4359,7 +4359,7 @@
"dependencies": {
"json5": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
}
@ -9536,7 +9536,7 @@
},
"load-json-file": {
"version": "2.0.0",
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"requires": {
"graceful-fs": "^4.1.2",
@ -9677,7 +9677,7 @@
},
"lodash.isempty": {
"version": "4.4.0",
"resolved": "http://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz",
"resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz",
"integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4="
},
"lodash.isequal": {
@ -10261,7 +10261,7 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"mixin-deep": {
@ -10285,7 +10285,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
@ -10293,7 +10293,7 @@
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
@ -11942,7 +11942,7 @@
"dependencies": {
"minimist": {
"version": "0.0.10",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
},
"wordwrap": {
@ -11997,14 +11997,14 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
}
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"output-file-sync": {
@ -12150,7 +12150,7 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"path-is-inside": {
@ -12200,7 +12200,7 @@
},
"pify": {
"version": "2.3.0",
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
},
"pirates": {
@ -12811,10 +12811,6 @@
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-1.1.1.tgz",
"integrity": "sha512-t0Ujm177bagjUOSzhpmkSz+LqFW04HnY9TeZFavDCmV521fQvFz82aD+POXqWsAdsJVOK3umJYBNNqCjC3g0hQ=="
},
"react-native-recyclerview-list": {
"version": "github:mattermost/react-native-recyclerview-list#a2d26bc7b2623cae25e947ba3905f702434717bf",
"from": "github:mattermost/react-native-recyclerview-list#a2d26bc7b2623cae25e947ba3905f702434717bf"
},
"react-native-safe-area": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/react-native-safe-area/-/react-native-safe-area-0.5.0.tgz",
@ -12926,7 +12922,7 @@
"dependencies": {
"core-js": {
"version": "1.2.7",
"resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
},
"fbjs": {
@ -13091,7 +13087,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@ -14089,7 +14085,7 @@
},
"safe-regex": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
"integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
"requires": {
"ret": "~0.1.10"
@ -14438,7 +14434,7 @@
"dependencies": {
"async": {
"version": "2.0.0",
"resolved": "http://registry.npmjs.org/async/-/async-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/async/-/async-2.0.0.tgz",
"integrity": "sha1-0JAK04WvE4BFQKEJxCFm4657K50=",
"dev": true,
"requires": {
@ -14537,7 +14533,7 @@
},
"serialize-error": {
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
"integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go="
},
"serve-static": {
@ -14658,7 +14654,7 @@
},
"xmlbuilder": {
"version": "8.2.2",
"resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
"integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M="
}
}
@ -14880,7 +14876,7 @@
},
"inquirer": {
"version": "5.2.0",
"resolved": "http://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz",
"integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==",
"dev": true,
"requires": {
@ -15283,7 +15279,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
@ -15296,7 +15292,7 @@
},
"strip-eof": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
},
"strip-json-comments": {
@ -15501,7 +15497,7 @@
},
"through": {
"version": "2.3.8",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"through2": {
@ -16237,7 +16233,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": {
"string-width": "^1.0.1",

View file

@ -46,7 +46,6 @@
"react-native-notifications": "github:mattermost/react-native-notifications#c58499aedf9bd4c9a405196ec807019ed316f2bb",
"react-native-passcode-status": "1.1.1",
"react-native-permissions": "1.1.1",
"react-native-recyclerview-list": "mattermost/react-native-recyclerview-list#a2d26bc7b2623cae25e947ba3905f702434717bf",
"react-native-safe-area": "0.5.0",
"react-native-section-list-get-item-layout": "2.2.3",
"react-native-sentry": "0.42.0",