diff --git a/app/lib/realm.js b/app/lib/realm.js
index 4b197e891..cbb8a5cb9 100644
--- a/app/lib/realm.js
+++ b/app/lib/realm.js
@@ -441,6 +441,9 @@ class DB {
setActiveDB(database = '') {
const path = database.replace(/(^\w+:|^)\/\//, '');
+ if (this.database) {
+ this.database.close();
+ }
return this.databases.activeDB = new Realm({
path: `${ path }.realm`,
schema,
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index 3afb583a7..7625453f9 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -8,7 +8,6 @@ import { RectButton } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal';
import moment from 'moment';
-import 'react-native-console-time-polyfill';
import EJSON from 'ejson';
import {
diff --git a/app/views/RoomsListView/ListHeader/SearchBar.js b/app/views/RoomsListView/ListHeader/SearchBar.js
new file mode 100644
index 000000000..31e0372d3
--- /dev/null
+++ b/app/views/RoomsListView/ListHeader/SearchBar.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import SearchBox from '../../../containers/SearchBox';
+import { isIOS } from '../../../utils/deviceInfo';
+
+const SearchBar = React.memo(({ onChangeSearchText }) => {
+ if (isIOS) {
+ return ;
+ }
+ return null;
+});
+
+SearchBar.propTypes = {
+ onChangeSearchText: PropTypes.func
+};
+
+export default SearchBar;
diff --git a/app/views/RoomsListView/ListHeader/Sort.js b/app/views/RoomsListView/ListHeader/Sort.js
new file mode 100644
index 000000000..087724289
--- /dev/null
+++ b/app/views/RoomsListView/ListHeader/Sort.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import { View, Text } from 'react-native';
+import PropTypes from 'prop-types';
+
+import { CustomIcon } from '../../../lib/Icons';
+import I18n from '../../../i18n';
+import Touch from '../../../utils/touch';
+import styles from '../styles';
+
+
+const Sort = React.memo(({ searchLength, sortBy, toggleSort }) => {
+ if (searchLength > 0) {
+ return null;
+ }
+ return (
+
+
+ {I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })}
+
+
+
+ );
+});
+
+Sort.propTypes = {
+ searchLength: PropTypes.number,
+ sortBy: PropTypes.string,
+ toggleSort: PropTypes.func
+};
+
+export default Sort;
diff --git a/app/views/RoomsListView/ListHeader/index.js b/app/views/RoomsListView/ListHeader/index.js
new file mode 100644
index 000000000..92743b39d
--- /dev/null
+++ b/app/views/RoomsListView/ListHeader/index.js
@@ -0,0 +1,23 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import SearchBar from './SearchBar';
+import Sort from './Sort';
+
+const ListHeader = React.memo(({
+ searchLength, sortBy, onChangeSearchText, toggleSort
+}) => (
+
+
+
+
+));
+
+ListHeader.propTypes = {
+ searchLength: PropTypes.number,
+ sortBy: PropTypes.string,
+ onChangeSearchText: PropTypes.func,
+ toggleSort: PropTypes.func
+};
+
+export default ListHeader;
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index 20785a7df..bffa904cc 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -1,14 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
- View, FlatList, BackHandler, ActivityIndicator, Text, ScrollView, Keyboard, LayoutAnimation
+ View, FlatList, BackHandler, ActivityIndicator, Text, ScrollView, Keyboard, LayoutAnimation, InteractionManager
} from 'react-native';
import { connect } from 'react-redux';
import { isEqual } from 'lodash';
import { SafeAreaView, NavigationEvents } from 'react-navigation';
import Orientation from 'react-native-orientation-locker';
-import SearchBox from '../../containers/SearchBox';
import ConnectionBadge from '../../containers/ConnectionBadge';
import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat';
@@ -19,7 +18,6 @@ import log from '../../utils/log';
import I18n from '../../i18n';
import SortDropdown from './SortDropdown';
import ServerDropdown from './ServerDropdown';
-import Touch from '../../utils/touch';
import {
toggleSortDropdown as toggleSortDropdownAction,
openSearchHeader as openSearchHeaderAction,
@@ -29,10 +27,10 @@ import {
import { appStart as appStartAction } from '../../actions';
import debounce from '../../utils/debounce';
import { isIOS, isAndroid } from '../../utils/deviceInfo';
-import { CustomIcon } from '../../lib/Icons';
import RoomsListHeaderView from './Header';
import { DrawerButton, CustomHeaderButtons, Item } from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar';
+import ListHeader from './ListHeader';
const SCROLL_OFFSET = 56;
@@ -119,6 +117,8 @@ export default class RoomsListView extends LoggedView {
constructor(props) {
super('RoomsListView', props);
+ console.time(`${ this.constructor.name } init`);
+ console.time(`${ this.constructor.name } mount`);
this.data = [];
this.state = {
@@ -143,6 +143,7 @@ export default class RoomsListView extends LoggedView {
navigation.setParams({
onPressItem: this._onPressItem, initSearchingAndroid: this.initSearchingAndroid, cancelSearchingAndroid: this.cancelSearchingAndroid
});
+ console.timeEnd(`${ this.constructor.name } mount`);
}
componentWillReceiveProps(nextProps) {
@@ -240,16 +241,19 @@ export default class RoomsListView extends LoggedView {
}
componentWillUnmount() {
- this.removeListener(this.data);
- this.removeListener(this.unread);
- this.removeListener(this.favorites);
- this.removeListener(this.discussions);
- this.removeListener(this.channels);
- this.removeListener(this.privateGroup);
- this.removeListener(this.direct);
- this.removeListener(this.livechat);
+ if (this.data && this.data.removeAllListeners) {
+ this.data.removeAllListeners();
+ }
+ if (this.getSubscriptions && this.getSubscriptions.stop) {
+ this.getSubscriptions.stop();
+ }
+ if (this.updateStateInteraction && this.updateStateInteraction.cancel) {
+ this.updateStateInteraction.cancel();
+ }
+ console.countReset(`${ this.constructor.name }.render calls`);
}
+ // eslint-disable-next-line react/sort-comp
internalSetState = (...args) => {
const { navigation } = this.props;
if (isIOS && navigation.isFocused()) {
@@ -258,7 +262,11 @@ export default class RoomsListView extends LoggedView {
this.setState(...args);
}
- getSubscriptions = () => {
+ getSubscriptions = debounce(() => {
+ if (this.data && this.data.removeAllListeners) {
+ this.data.removeAllListeners();
+ }
+
const {
server, sortBy, showUnread, showFavorites, groupByType
} = this.props;
@@ -271,92 +279,57 @@ export default class RoomsListView extends LoggedView {
this.data = this.data.sorted('roomUpdatedAt', true);
}
- let chats = [];
- let unread = [];
- let favorites = [];
- let discussions = [];
- let channels = [];
- let privateGroup = [];
- let direct = [];
- let livechat = [];
-
// unread
if (showUnread) {
this.unread = this.data.filtered('(unread > 0 || alert == true)');
- unread = this.removeRealmInstance(this.unread);
- safeAddListener(this.unread, debounce(() => this.internalSetState({ unread: this.removeRealmInstance(this.unread) }), 300));
} else {
- this.removeListener(unread);
+ this.unread = [];
}
// favorites
if (showFavorites) {
this.favorites = this.data.filtered('f == true');
- favorites = this.removeRealmInstance(this.favorites);
- safeAddListener(this.favorites, debounce(() => this.internalSetState({ favorites: this.removeRealmInstance(this.favorites) }), 300));
} else {
- this.removeListener(favorites);
+ this.favorites = [];
}
// type
if (groupByType) {
- // discussions
this.discussions = this.data.filtered('prid != null');
- discussions = this.removeRealmInstance(this.discussions);
-
- // channels
this.channels = this.data.filtered('t == $0 AND prid == null', 'c');
- channels = this.removeRealmInstance(this.channels);
-
- // private
this.privateGroup = this.data.filtered('t == $0 AND prid == null', 'p');
- privateGroup = this.removeRealmInstance(this.privateGroup);
-
- // direct
this.direct = this.data.filtered('t == $0 AND prid == null', 'd');
- direct = this.removeRealmInstance(this.direct);
-
- // livechat
this.livechat = this.data.filtered('t == $0 AND prid == null', 'l');
- livechat = this.removeRealmInstance(this.livechat);
-
- safeAddListener(this.discussions, debounce(() => this.internalSetState({ discussions: this.removeRealmInstance(this.discussions) }), 300));
- safeAddListener(this.channels, debounce(() => this.internalSetState({ channels: this.removeRealmInstance(this.channels) }), 300));
- safeAddListener(this.privateGroup, debounce(() => this.internalSetState({ privateGroup: this.removeRealmInstance(this.privateGroup) }), 300));
- safeAddListener(this.direct, debounce(() => this.internalSetState({ direct: this.removeRealmInstance(this.direct) }), 300));
- safeAddListener(this.livechat, debounce(() => this.internalSetState({ livechat: this.removeRealmInstance(this.livechat) }), 300));
- this.removeListener(this.chats);
+ } else if (showUnread) {
+ this.chats = this.data.filtered('(unread == 0 && alert == false)');
} else {
- // chats
- if (showUnread) {
- this.chats = this.data.filtered('(unread == 0 && alert == false)');
- } else {
- this.chats = this.data;
- }
- chats = this.removeRealmInstance(this.chats);
-
- safeAddListener(this.chats, debounce(() => this.internalSetState({ chats: this.removeRealmInstance(this.chats) }), 300));
- this.removeListener(this.discussions);
- this.removeListener(this.channels);
- this.removeListener(this.privateGroup);
- this.removeListener(this.direct);
- this.removeListener(this.livechat);
+ this.chats = this.data;
}
+ safeAddListener(this.data, this.updateState);
+ }
+ }, 300);
- // setState
+ // eslint-disable-next-line react/sort-comp
+ updateState = debounce(() => {
+ this.updateStateInteraction = InteractionManager.runAfterInteractions(() => {
this.internalSetState({
- chats, unread, favorites, discussions, channels, privateGroup, direct, livechat, loading: false
+ chats: this.getSnapshot(this.chats),
+ unread: this.getSnapshot(this.unread),
+ favorites: this.getSnapshot(this.favorites),
+ discussions: this.getSnapshot(this.discussions),
+ channels: this.getSnapshot(this.channels),
+ privateGroup: this.getSnapshot(this.privateGroup),
+ direct: this.getSnapshot(this.direct),
+ livechat: this.getSnapshot(this.livechat),
+ loading: false
});
- }
- }
+ });
+ }, 300);
- removeRealmInstance = (data) => {
- const array = Array.from(data);
- return JSON.parse(JSON.stringify(array));
- }
-
- removeListener = (data) => {
- if (data && data.removeAllListeners) {
- data.removeAllListeners();
+ getSnapshot = (data) => {
+ if (data && data.length) {
+ const array = Array.from(data);
+ return JSON.parse(JSON.stringify(array));
}
+ return [];
}
initSearchingAndroid = () => {
@@ -449,44 +422,19 @@ export default class RoomsListView extends LoggedView {
getScrollRef = ref => this.scroll = ref
- renderHeader = () => {
+ renderListHeader = () => {
const { search } = this.state;
- if (search.length > 0) {
- return null;
- }
- return this.renderSort();
- }
-
- renderSort = () => {
const { sortBy } = this.props;
-
return (
-
-
- {I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })}
-
-
-
+
);
}
- renderSearchBar = () => {
- if (isIOS) {
- return ;
- }
- }
-
- renderListHeader = () => (
- [
- this.renderSearchBar(),
- this.renderHeader()
- ]
- )
-
renderItem = ({ item }) => {
const {
userId, baseUrl, StoreLastMessage
@@ -634,6 +582,7 @@ export default class RoomsListView extends LoggedView {
}
render = () => {
+ console.count(`${ this.constructor.name }.render calls`);
const {
sortBy, groupByType, showFavorites, showUnread, showServerDropdown, showSortDropdown
} = this.props;
@@ -654,7 +603,7 @@ export default class RoomsListView extends LoggedView {
)
: null
}
- {showServerDropdown ? : null}
+ {showServerDropdown ? : null}
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress)}
diff --git a/index.js b/index.js
index ba4623382..959bbd7a4 100644
--- a/index.js
+++ b/index.js
@@ -1,3 +1,5 @@
+import 'react-native-console-time-polyfill';
+
import './app/ReactotronConfig';
import { AppRegistry } from 'react-native';
import App from './app/index';