From 9dbe10bcf871392ed09b512152bc921578150e9f Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 20 Jul 2020 13:44:54 -0300 Subject: [PATCH] [IMPROVEMENT] Native sort and limit queries (#2249) * Update WatermelonDB to 0.18.0 * Low onEndReachedThreshold * Query experiment * QUERY_SIZE * Query or fetch data * Reorder class functions * Reset variables * Hide system messages * Change this.count behaviour * Starting on RoomsListView * unsubscribeQuery * onEndReached * Separate queries * Reusable where clause * Refactoring * Refactor RoomItem to accept item as prop * Comment RoomItem tests just so jest passes * Fix alert and status * onPress * Unnecessary diff * react-fast-compare * Native limit on ShareListView * Tweak item description * Lint * Fix on foreground crash * Suggested changes --- .../__snapshots__/Storyshots.test.js.snap | 131 -------------- app/lib/rocketchat.js | 6 + app/presentation/RoomItem/index.js | 138 +++++++------- app/views/RoomView/List.js | 170 ++++++++++-------- app/views/RoomView/index.js | 13 +- app/views/RoomsListView/index.js | 155 ++++++++-------- app/views/ShareListView/index.js | 72 +++++--- package.json | 3 +- patches/@nozbe+watermelondb+0.16.2.patch | 20 --- storybook/stories/index.js | 34 ++-- yarn.lock | 27 ++- 11 files changed, 339 insertions(+), 430 deletions(-) delete mode 100644 patches/@nozbe+watermelondb+0.16.2.patch diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index e881064aa..4b04cc4a8 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -4994,137 +4994,6 @@ exports[`Storyshots Message list message 1`] = ` `; -exports[`Storyshots RoomItem list roomitem 1`] = ` - - - - Basic - - View - - User - - View - View - - Type - - View - View - View - View - View - - Alerts - - View - View - View - View - View - View - View - View - View - - Last Message - - View - View - View - View - View - View - - -`; - exports[`Storyshots UiKitMessage list uikitmessage 1`] = ` 0 || item.alert === true); // either its unread count > 0 or its alert + return !isUnread; + }, + isGroupChat(room) { return (room.uids && room.uids.length > 2) || (room.usernames && room.usernames.length > 2); }, diff --git a/app/presentation/RoomItem/index.js b/app/presentation/RoomItem/index.js index 1b3ee93f2..487c22430 100644 --- a/app/presentation/RoomItem/index.js +++ b/app/presentation/RoomItem/index.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { View, Text } from 'react-native'; import { connect } from 'react-redux'; @@ -16,82 +16,79 @@ import { themes } from '../../constants/colors'; export { ROW_HEIGHT }; const attrs = [ - 'name', - 'unread', - 'userMentions', - 'showLastMessage', - 'useRealName', - 'alert', - 'type', 'width', - 'isRead', - 'favorite', 'status', 'connected', 'theme', - 'isFocused' + 'isFocused', + 'forceUpdate', + 'showLastMessage' ]; -const arePropsEqual = (oldProps, newProps) => { - const { _updatedAt: _updatedAtOld } = oldProps; - const { _updatedAt: _updatedAtNew } = newProps; - if (_updatedAtOld && _updatedAtNew && _updatedAtOld.toISOString() !== _updatedAtNew.toISOString()) { - return false; - } - return attrs.every(key => oldProps[key] === newProps[key]); -}; +const arePropsEqual = (oldProps, newProps) => attrs.every(key => oldProps[key] === newProps[key]); const RoomItem = React.memo(({ + item, onPress, width, - favorite, toggleFav, - isRead, - rid, toggleRead, hideChannel, testID, - unread, - userMentions, - name, - _updatedAt, - alert, - type, avatarSize, baseUrl, userId, username, token, id, - prid, showLastMessage, - hideUnreadStatus, - lastMessage, status, - avatar, useRealName, getUserPresence, - isGroupChat, connected, theme, - isFocused + isFocused, + getRoomTitle, + getRoomAvatar, + getIsGroupChat, + getIsRead }) => { + const [, setForceUpdate] = useState(1); + useEffect(() => { - if (connected && type === 'd' && id) { + if (connected && item.t === 'd' && id) { getUserPresence(id); } }, [connected]); - const date = lastMessage && formatDate(lastMessage.ts); + useEffect(() => { + if (item?.observe) { + const observable = item.observe(); + const subscription = observable?.subscribe?.(() => { + setForceUpdate(prevForceUpdate => prevForceUpdate + 1); + }); + + return () => { + subscription?.unsubscribe?.(); + }; + } + }, []); + + const name = getRoomTitle(item); + const avatar = getRoomAvatar(item); + const isGroupChat = getIsGroupChat(item); + const isRead = getIsRead(item); + const _onPress = () => onPress(item); + const date = item.lastMessage?.ts && formatDate(item.lastMessage.ts); let accessibilityLabel = name; - if (unread === 1) { - accessibilityLabel += `, ${ unread } ${ I18n.t('alert') }`; - } else if (unread > 1) { - accessibilityLabel += `, ${ unread } ${ I18n.t('alerts') }`; + if (item.unread === 1) { + accessibilityLabel += `, ${ item.unread } ${ I18n.t('alert') }`; + } else if (item.unread > 1) { + accessibilityLabel += `, ${ item.unread } ${ I18n.t('alerts') }`; } - if (userMentions > 0) { + if (item.userMentions > 0) { accessibilityLabel += `, ${ I18n.t('you_were_mentioned') }`; } @@ -101,16 +98,16 @@ const RoomItem = React.memo(({ return ( @@ -121,7 +118,7 @@ const RoomItem = React.memo(({ {name} - {_updatedAt ? ( + {item.roomUpdatedAt ? ( @@ -203,17 +200,10 @@ const RoomItem = React.memo(({ }, arePropsEqual); RoomItem.propTypes = { - type: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, + item: PropTypes.object.isRequired, baseUrl: PropTypes.string.isRequired, showLastMessage: PropTypes.bool, - _updatedAt: PropTypes.string, - lastMessage: PropTypes.object, - alert: PropTypes.bool, - unread: PropTypes.number, - userMentions: PropTypes.number, id: PropTypes.string, - prid: PropTypes.string, onPress: PropTypes.func, userId: PropTypes.string, username: PropTypes.string, @@ -221,27 +211,29 @@ RoomItem.propTypes = { avatarSize: PropTypes.number, testID: PropTypes.string, width: PropTypes.number, - favorite: PropTypes.bool, - isRead: PropTypes.bool, - rid: PropTypes.string, status: PropTypes.string, toggleFav: PropTypes.func, toggleRead: PropTypes.func, hideChannel: PropTypes.func, - avatar: PropTypes.bool, - hideUnreadStatus: PropTypes.bool, useRealName: PropTypes.bool, getUserPresence: PropTypes.func, connected: PropTypes.bool, - isGroupChat: PropTypes.bool, theme: PropTypes.string, - isFocused: PropTypes.bool + isFocused: PropTypes.bool, + getRoomTitle: PropTypes.func, + getRoomAvatar: PropTypes.func, + getIsGroupChat: PropTypes.func, + getIsRead: PropTypes.func }; RoomItem.defaultProps = { avatarSize: 48, status: 'offline', - getUserPresence: () => {} + getUserPresence: () => {}, + getRoomTitle: () => 'title', + getRoomAvatar: () => '', + getIsGroupChat: () => false, + getIsRead: () => false }; const mapStateToProps = (state, ownProps) => { diff --git a/app/views/RoomView/List.js b/app/views/RoomView/List.js index d0dc7c88d..209ec18bb 100644 --- a/app/views/RoomView/List.js +++ b/app/views/RoomView/List.js @@ -1,7 +1,6 @@ import React from 'react'; import { FlatList, RefreshControl } from 'react-native'; import PropTypes from 'prop-types'; -import orderBy from 'lodash/orderBy'; import { Q } from '@nozbe/watermelondb'; import moment from 'moment'; import isEqual from 'lodash/isEqual'; @@ -15,9 +14,10 @@ import EmptyRoom from './EmptyRoom'; import { isIOS } from '../../utils/deviceInfo'; import { animateNextTransition } from '../../utils/layoutAnimation'; import ActivityIndicator from '../../containers/ActivityIndicator'; -import debounce from '../../utils/debounce'; import { themes } from '../../constants/colors'; +const QUERY_SIZE = 50; + class List extends React.Component { static propTypes = { onEndReached: PropTypes.func, @@ -47,7 +47,8 @@ class List extends React.Component { super(props); console.time(`${ this.constructor.name } init`); console.time(`${ this.constructor.name } mount`); - + this.count = 0; + this.needsFetch = false; this.mounted = false; this.state = { loading: true, @@ -56,7 +57,7 @@ class List extends React.Component { refreshing: false, animated: false }; - this.init(); + this.query(); this.unsubscribeFocus = props.navigation.addListener('focus', () => { if (this.mounted) { this.setState({ animated: true }); @@ -72,72 +73,6 @@ class List extends React.Component { console.timeEnd(`${ this.constructor.name } mount`); } - // eslint-disable-next-line react/sort-comp - async init() { - const { rid, tmid } = this.props; - const db = database.active; - - // handle servers with version < 3.0.0 - let { hideSystemMessages = [] } = this.props; - if (!Array.isArray(hideSystemMessages)) { - hideSystemMessages = []; - } - - if (tmid) { - try { - this.thread = await db.collections - .get('threads') - .find(tmid); - } catch (e) { - console.log(e); - } - this.messagesObservable = db.collections - .get('thread_messages') - .query(Q.where('rid', tmid), Q.or(Q.where('t', Q.notIn(hideSystemMessages)), Q.where('t', Q.eq(null)))) - .observe(); - } else if (rid) { - this.messagesObservable = db.collections - .get('messages') - .query(Q.where('rid', rid), Q.or(Q.where('t', Q.notIn(hideSystemMessages)), Q.where('t', Q.eq(null)))) - .observe(); - } - - if (rid) { - this.unsubscribeMessages(); - this.messagesSubscription = this.messagesObservable - .subscribe((data) => { - if (tmid && this.thread) { - data = [this.thread, ...data]; - } - const messages = orderBy(data, ['ts'], ['desc']); - if (this.mounted) { - this.setState({ messages }, () => this.update()); - } else { - this.state.messages = messages; - } - this.readThreads(); - }); - } - } - - // eslint-disable-next-line react/sort-comp - reload = () => { - this.unsubscribeMessages(); - this.init(); - } - - readThreads = async() => { - const { tmid } = this.props; - - if (tmid) { - try { - await RocketChat.readThreads(tmid); - } catch { - // Do nothing - } - } - } - shouldComponentUpdate(nextProps, nextState) { const { loading, end, refreshing } = this.state; const { hideSystemMessages, theme } = this.props; @@ -177,7 +112,7 @@ class List extends React.Component { console.countReset(`${ this.constructor.name }.render calls`); } - onEndReached = debounce(async() => { + fetchData = async() => { const { loading, end, messages, latest = messages[messages.length - 1]?.ts } = this.state; @@ -196,12 +131,99 @@ class List extends React.Component { result = await RocketChat.loadMessagesForRoom({ rid, t, latest }); } - this.setState({ end: result.length < 50, loading: false, latest: result[result.length - 1]?.ts }, () => this.loadMoreMessages(result)); + this.setState({ end: result.length < QUERY_SIZE, loading: false, latest: result[result.length - 1]?.ts }, () => this.loadMoreMessages(result)); } catch (e) { this.setState({ loading: false }); log(e); } - }, 300) + } + + query = async() => { + this.count += QUERY_SIZE; + const { rid, tmid } = this.props; + const db = database.active; + + // handle servers with version < 3.0.0 + let { hideSystemMessages = [] } = this.props; + if (!Array.isArray(hideSystemMessages)) { + hideSystemMessages = []; + } + + if (tmid) { + try { + this.thread = await db.collections + .get('threads') + .find(tmid); + } catch (e) { + console.log(e); + } + this.messagesObservable = db.collections + .get('thread_messages') + .query( + Q.where('rid', tmid), + Q.experimentalSortBy('ts', Q.desc), + Q.experimentalSkip(0), + Q.experimentalTake(this.count) + ) + .observe(); + } else if (rid) { + this.messagesObservable = db.collections + .get('messages') + .query( + Q.where('rid', rid), + Q.experimentalSortBy('ts', Q.desc), + Q.experimentalSkip(0), + Q.experimentalTake(this.count) + ) + .observe(); + } + + if (rid) { + this.unsubscribeMessages(); + this.messagesSubscription = this.messagesObservable + .subscribe((messages) => { + if (messages.length <= this.count) { + this.needsFetch = true; + } + if (tmid && this.thread) { + messages = [...messages, this.thread]; + } + messages = messages.filter(m => !m.t || !hideSystemMessages?.includes(m.t)); + + if (this.mounted) { + this.setState({ messages }, () => this.update()); + } else { + this.state.messages = messages; + } + this.readThreads(); + }); + } + } + + reload = () => { + this.count = 0; + this.query(); + } + + readThreads = async() => { + const { tmid } = this.props; + + if (tmid) { + try { + await RocketChat.readThreads(tmid); + } catch { + // Do nothing + } + } + } + + onEndReached = async() => { + if (this.needsFetch) { + this.needsFetch = false; + await this.fetchData(); + } + this.query(); + } loadMoreMessages = (result) => { const { end } = this.state; @@ -305,7 +327,7 @@ class List extends React.Component { removeClippedSubviews={isIOS} initialNumToRender={7} onEndReached={this.onEndReached} - onEndReachedThreshold={5} + onEndReachedThreshold={0.5} maxToRenderPerBatch={5} windowSize={10} ListFooterComponent={this.renderFooter} diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 0b6afd18f..4a54117dd 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -206,12 +206,10 @@ class RoomView extends React.Component { const { appState, insets } = this.props; if (appState === 'foreground' && appState !== prevProps.appState && this.rid) { - this.onForegroundInteraction = InteractionManager.runAfterInteractions(() => { - // Fire List.init() just to keep observables working - if (this.list && this.list.current) { - this.list.current.init(); - } - }); + // Fire List.query() just to keep observables working + if (this.list && this.list.current) { + this.list.current?.query?.(); + } } // If it's not direct message if (this.t !== 'd') { @@ -267,9 +265,6 @@ class RoomView extends React.Component { if (this.didMountInteraction && this.didMountInteraction.cancel) { this.didMountInteraction.cancel(); } - if (this.onForegroundInteraction && this.onForegroundInteraction.cancel) { - this.onForegroundInteraction.cancel(); - } if (this.willBlurListener && this.willBlurListener.remove) { this.willBlurListener.remove(); } diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 232ea3228..d2bd6cced 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -9,7 +9,7 @@ import { RefreshControl } from 'react-native'; import { connect } from 'react-redux'; -import { isEqual, orderBy } from 'lodash'; +import isEqual from 'react-fast-compare'; import Orientation from 'react-native-orientation-locker'; import { Q } from '@nozbe/watermelondb'; import { withSafeAreaInsets } from 'react-native-safe-area-context'; @@ -71,6 +71,7 @@ const DISCUSSIONS_HEADER = 'Discussions'; const CHANNELS_HEADER = 'Channels'; const DM_HEADER = 'Direct_Messages'; const GROUPS_HEADER = 'Private_Groups'; +const QUERY_SIZE = 20; const filterIsUnread = s => (s.unread > 0 || s.alert) && !s.hideUnreadStatus; const filterIsFavorite = s => s.f; @@ -140,11 +141,12 @@ class RoomsListView extends React.Component { this.gotSubscriptions = false; this.animated = false; + this.count = 0; this.state = { searching: false, search: [], loading: true, - allChats: [], + chatsOrder: [], chats: [], item: {} }; @@ -211,7 +213,7 @@ class RoomsListView extends React.Component { } shouldComponentUpdate(nextProps, nextState) { - const { allChats, searching, item } = this.state; + const { chatsOrder, searching, item } = this.state; // eslint-disable-next-line react/destructuring-assignment const propsUpdated = shouldUpdateProps.some(key => nextProps[key] !== this.props[key]); if (propsUpdated) { @@ -219,7 +221,7 @@ class RoomsListView extends React.Component { } // Compare changes only once - const chatsNotEqual = !isEqual(nextState.allChats, allChats); + const chatsNotEqual = !isEqual(nextState.chatsOrder, chatsOrder); // If they aren't equal, set to update if focused if (chatsNotEqual) { @@ -290,7 +292,7 @@ class RoomsListView extends React.Component { && prevProps.showUnread === showUnread ) ) { - this.getSubscriptions(true); + this.getSubscriptions(); } else if ( appState === 'foreground' && appState !== prevProps.appState @@ -309,9 +311,7 @@ class RoomsListView extends React.Component { } componentWillUnmount() { - if (this.querySubscription && this.querySubscription.unsubscribe) { - this.querySubscription.unsubscribe(); - } + this.unsubscribeQuery(); if (this.unsubscribeFocus) { this.unsubscribeFocus(); } @@ -396,17 +396,8 @@ class RoomsListView extends React.Component { return allData; } - getSubscriptions = async(force = false) => { - if (this.gotSubscriptions && !force) { - return; - } - this.gotSubscriptions = true; - - if (this.querySubscription && this.querySubscription.unsubscribe) { - this.querySubscription.unsubscribe(); - } - - this.setState({ loading: true }); + getSubscriptions = async() => { + this.unsubscribeQuery(); const { sortBy, @@ -416,41 +407,49 @@ class RoomsListView extends React.Component { } = this.props; const db = database.active; - const observable = await db.collections - .get('subscriptions') - .query( - Q.where('archived', false), - Q.where('open', true) - ) - .observeWithColumns(['room_updated_at', 'unread', 'alert', 'user_mentions', 'f', 't']); + let observable; + + const defaultWhereClause = [ + Q.where('archived', false), + Q.where('open', true) + ]; + + if (sortBy === 'alphabetical') { + defaultWhereClause.push(Q.experimentalSortBy(`${ this.useRealName ? 'fname' : 'name' }`, Q.asc)); + } else { + defaultWhereClause.push(Q.experimentalSortBy('room_updated_at', Q.desc)); + } + + // When we're grouping by something + if (this.isGrouping) { + observable = await db.collections + .get('subscriptions') + .query(...defaultWhereClause) + .observe(); + + // When we're NOT grouping + } else { + this.count += QUERY_SIZE; + observable = await db.collections + .get('subscriptions') + .query( + ...defaultWhereClause, + Q.experimentalSkip(0), + Q.experimentalTake(this.count) + ) + .observe(); + } + this.querySubscription = observable.subscribe((data) => { let tempChats = []; - let chats = []; - if (sortBy === 'alphabetical') { - chats = orderBy(data, [`${ this.useRealName ? 'fname' : 'name' }`], ['asc']); - } else { - chats = orderBy(data, ['roomUpdatedAt'], ['desc']); - } + let chats = data; - // it's better to map and test all subs altogether then testing them individually - const allChats = data.map(item => ({ - alert: item.alert, - unread: item.unread, - userMentions: item.userMentions, - isRead: this.getIsRead(item), - favorite: item.f, - lastMessage: item.lastMessage, - name: this.getRoomTitle(item), - _updatedAt: item.roomUpdatedAt, - key: item._id, - rid: item.rid, - type: item.t, - prid: item.prid, - uids: item.uids, - usernames: item.usernames, - visitor: item.visitor - })); + /** + * We trigger re-render only when chats order changes + * RoomItem handles its own re-render + */ + const chatsOrder = data.map(item => item.rid); // unread if (showUnread) { @@ -484,12 +483,18 @@ class RoomsListView extends React.Component { this.internalSetState({ chats: tempChats, - allChats, + chatsOrder, loading: false }); }); } + unsubscribeQuery = () => { + if (this.querySubscription && this.querySubscription.unsubscribe) { + this.querySubscription.unsubscribe(); + } + } + initSearching = () => { const { openSearchHeader } = this.props; this.internalSetState({ searching: true }, () => { @@ -548,10 +553,19 @@ class RoomsListView extends React.Component { getRoomAvatar = item => RocketChat.getRoomAvatar(item) + isGroupChat = item => RocketChat.isGroupChat(item) + + isRead = item => RocketChat.isRead(item) + getUserPresence = uid => RocketChat.getUserPresence(uid) getUidDirectMessage = room => RocketChat.getUidDirectMessage(room); + get isGrouping() { + const { showUnread, showFavorites, groupByType } = this.props; + return showUnread || showFavorites || groupByType; + } + onPressItem = (item = {}) => { const { navigation, isMasterDetail } = this.props; if (!navigation.isFocused()) { @@ -743,6 +757,13 @@ class RoomsListView extends React.Component { roomsRequest({ allData: true }); } + onEndReached = () => { + // Run only when we're not grouping by anything + if (!this.isGrouping) { + this.getSubscriptions(); + } + } + getScrollRef = ref => (this.scroll = ref); renderListHeader = () => { @@ -774,12 +795,6 @@ class RoomsListView extends React.Component { ); } - getIsRead = (item) => { - let isUnread = item.archived !== true && item.open === true; // item is not archived and not opened - isUnread = isUnread && (item.unread > 0 || item.alert === true); // either its unread count > 0 or its alert - return !isUnread; - }; - renderItem = ({ item }) => { if (item.separator) { return this.renderSectionHeader(item.rid); @@ -800,32 +815,19 @@ class RoomsListView extends React.Component { width } = this.props; const id = this.getUidDirectMessage(item); - const isGroupChat = RocketChat.isGroupChat(item); return ( this.onPressItem(item)} + onPress={this.onPressItem} testID={`rooms-list-view-item-${ item.name }`} width={isMasterDetail ? MAX_SIDEBAR_WIDTH : width} toggleFav={this.toggleFav} @@ -833,7 +835,10 @@ class RoomsListView extends React.Component { hideChannel={this.hideChannel} useRealName={useRealName} getUserPresence={this.getUserPresence} - isGroupChat={isGroupChat} + getRoomTitle={this.getRoomTitle} + getRoomAvatar={this.getRoomAvatar} + getIsGroupChat={this.isGroupChat} + getIsRead={this.isRead} visitor={item.visitor} isFocused={currentItem?.rid === item.rid} /> @@ -880,6 +885,8 @@ class RoomsListView extends React.Component { /> )} windowSize={9} + onEndReached={this.onEndReached} + onEndReachedThreshold={0.5} /> ); }; diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js index 7e81998cb..2be4ee122 100644 --- a/app/views/ShareListView/index.js +++ b/app/views/ShareListView/index.js @@ -7,7 +7,7 @@ import ShareExtension from 'rn-extensions-share'; import * as FileSystem from 'expo-file-system'; import { connect } from 'react-redux'; import * as mime from 'react-native-mime-types'; -import { isEqual, orderBy } from 'lodash'; +import isEqual from 'react-fast-compare'; import { Q } from '@nozbe/watermelondb'; import database from '../../lib/database'; @@ -32,7 +32,6 @@ const permission = { message: I18n.t('Read_External_Permission_Message') }; -const LIMIT = 50; const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); const keyExtractor = item => item.rid; @@ -47,7 +46,7 @@ class ShareListView extends React.Component { constructor(props) { super(props); - this.data = []; + this.chats = []; this.state = { searching: false, searchText: '', @@ -186,22 +185,36 @@ class ShareListView extends React.Component { this.setState(...args); } - getSubscriptions = async(server) => { + query = (text) => { const db = database.active; + const defaultWhereClause = [ + Q.where('archived', false), + Q.where('open', true), + Q.experimentalSkip(0), + Q.experimentalTake(50), + Q.experimentalSortBy('room_updated_at', Q.desc) + ]; + if (text) { + return db.collections + .get('subscriptions') + .query( + ...defaultWhereClause, + Q.or( + Q.where('name', Q.like(`%${ Q.sanitizeLikeString(text) }%`)), + Q.where('fname', Q.like(`%${ Q.sanitizeLikeString(text) }%`)) + ) + ).fetch(); + } + return db.collections.get('subscriptions').query(...defaultWhereClause).fetch(); + } + + getSubscriptions = async(server) => { const serversDB = database.servers; if (server) { - this.data = await db.collections - .get('subscriptions') - .query( - Q.where('archived', false), - Q.where('open', true) - ).fetch(); - this.data = orderBy(this.data, ['roomUpdatedAt'], ['desc']); - + this.chats = await this.query(); const serversCollection = serversDB.collections.get('servers'); this.servers = await serversCollection.query().fetch(); - this.chats = this.data.slice(0, LIMIT); let serverInfo = {}; try { serverInfo = await serversCollection.find(server); @@ -210,8 +223,8 @@ class ShareListView extends React.Component { } this.internalSetState({ - chats: this.chats ? this.chats.slice() : [], - servers: this.servers ? this.servers.slice() : [], + chats: this.chats ?? [], + servers: this.servers ?? [], loading: false, serverInfo }); @@ -253,10 +266,10 @@ class ShareListView extends React.Component { }); } - search = (text) => { - const result = this.data.filter(item => item.name.includes(text)) || []; + search = async(text) => { + const result = await this.query(text); this.internalSetState({ - searchResults: result.slice(0, LIMIT), + searchResults: result, searchText: text }); } @@ -297,9 +310,26 @@ class ShareListView extends React.Component { } renderItem = ({ item }) => { + const { serverInfo } = this.state; + const { useRealName } = serverInfo; const { userId, token, server, theme } = this.props; + let description; + switch (item.t) { + case 'c': + description = item.topic || item.description; + break; + case 'p': + description = item.topic || item.description; + break; + case 'd': + description = useRealName ? item.name : item.fname; + break; + default: + description = item.fname; + break; + } return ( this.shareMessage(item)} testID={`share-extension-item-${ item.name }`} diff --git a/package.json b/package.json index 8b741c790..1f7dd4f17 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@codler/react-native-keyboard-aware-scroll-view": "^1.0.1", - "@nozbe/watermelondb": "0.16.2", + "@nozbe/watermelondb": "^0.18.0", "@react-native-community/art": "^1.2.0", "@react-native-community/async-storage": "1.11.0", "@react-native-community/cameraroll": "4.0.0", @@ -61,6 +61,7 @@ "pretty-bytes": "^5.3.0", "prop-types": "15.7.2", "react": "16.13.1", + "react-fast-compare": "^3.2.0", "react-native": "^0.63.1", "react-native-animatable": "^1.3.3", "react-native-appearance": "0.3.4", diff --git a/patches/@nozbe+watermelondb+0.16.2.patch b/patches/@nozbe+watermelondb+0.16.2.patch deleted file mode 100644 index 6cbdb5507..000000000 --- a/patches/@nozbe+watermelondb+0.16.2.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB.xcodeproj/project.pbxproj b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB.xcodeproj/project.pbxproj -index 63e6ef9..45c092b 100644 ---- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB.xcodeproj/project.pbxproj -+++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB.xcodeproj/project.pbxproj -@@ -374,6 +374,7 @@ - "$(SRCROOT)/../../../../node_modules/react-native/Libraries/**", - "$(SRCROOT)/../../../../../ios/Pods/Headers/Public/React-Core/**", - "$(SRCROOT)/../../../../../../native/ios/Pods/Headers/Public/React-Core", -+ "$(SRCROOT)/../../../../../ios/Pods/Headers/Public/React-jsi/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = ( -@@ -426,6 +427,7 @@ - "$(SRCROOT)/../../../../node_modules/react-native/Libraries/**", - "$(SRCROOT)/../../../../../ios/Pods/Headers/Public/React-Core/**", - "$(SRCROOT)/../../../../../../native/ios/Pods/Headers/Public/React-Core", -+ "$(SRCROOT)/../../../../../ios/Pods/Headers/Public/React-jsi/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = ( diff --git a/storybook/stories/index.js b/storybook/stories/index.js index cc5a4068f..882416c6c 100644 --- a/storybook/stories/index.js +++ b/storybook/stories/index.js @@ -1,10 +1,10 @@ /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ import React from 'react'; -import { Provider } from 'react-redux'; -import { createStore, combineReducers } from 'redux'; +// import { Provider } from 'react-redux'; +// import { createStore, combineReducers } from 'redux'; import { storiesOf } from '@storybook/react-native'; -import RoomItem from './RoomItem'; +// import RoomItem from './RoomItem'; import Message from './Message'; import UiKitMessage from './UiKitMessage'; import UiKitModal from './UiKitModal'; @@ -24,17 +24,17 @@ const user = { // Change here to see themed storybook const theme = 'light'; -const reducers = combineReducers({ - settings: () => ({}), - login: () => ({ - user: { - username: 'diego.mello' - } - }), - meteor: () => ({ connected: true }), - activeUsers: () => ({ abc: { status: 'online', statusText: 'dog' } }) -}); -const store = createStore(reducers); +// const reducers = combineReducers({ +// settings: () => ({}), +// login: () => ({ +// user: { +// username: 'diego.mello' +// } +// }), +// meteor: () => ({ connected: true }), +// activeUsers: () => ({ abc: { status: 'online', statusText: 'dog' } }) +// }); +// const store = createStore(reducers); const messageDecorator = story => ( ( ); -storiesOf('RoomItem', module) - .addDecorator(story => {story()}) - .add('list roomitem', () => ); +// storiesOf('RoomItem', module) +// .addDecorator(story => {story()}) +// .add('list roomitem', () => ); storiesOf('Message', module) .addDecorator(messageDecorator) .add('list message', () => ); diff --git a/yarn.lock b/yarn.lock index a60ebfcfa..8ea560782 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1917,13 +1917,19 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== -"@nozbe/watermelondb@0.16.2": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@nozbe/watermelondb/-/watermelondb-0.16.2.tgz#9c29c426bedb3d3af5d38304344bb49d60ddd1af" - integrity sha512-LLOEl13bVCiLsZf8QCHt5KUmuks7QnMtsXuhBB2rSb1vBDjc3mG2HmkH/eTzbgJnGIdFzH/XtoVUHlS/ll7Log== +"@nozbe/sqlite@3.31.1": + version "3.31.1" + resolved "https://registry.yarnpkg.com/@nozbe/sqlite/-/sqlite-3.31.1.tgz#ffd394ad7c188c6b73f89fd6e1ccb849a1b96dba" + integrity sha512-z5+GdcHZl9OQ1g0pnygORAnwCYUlYw/gQxdW/8rS0HxD2Gnn/k3DBQOvqQIH4Z3Z3KWVMbGUYhcH1v4SqTAdwg== + +"@nozbe/watermelondb@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@nozbe/watermelondb/-/watermelondb-0.18.0.tgz#f174d8a4b9e8665bb9e8a7825a682905a43cd92c" + integrity sha512-jybGScFR+b0wp8QDNJw9cfd7Qfu51SegivjIniX6dXV/KZxjjhQ+IJxUB6bp2f2tWJIQcxC5e9syctLsrh/PiQ== dependencies: + "@nozbe/sqlite" "3.31.1" lodash.clonedeep "^4.5.0" - lokijs "git+https://github.com/Nozbe/LokiJS.git#d08f660" + lokijs "git+https://github.com/Nozbe/LokiJS.git#fabe503" rambdax "2.15.0" rxjs "^6.2.2" rxjs-compat "^6.3.2" @@ -10206,9 +10212,9 @@ logkitty@^0.7.1: dayjs "^1.8.15" yargs "^15.1.0" -"lokijs@git+https://github.com/Nozbe/LokiJS.git#d08f660": - version "1.5.7" - resolved "git+https://github.com/Nozbe/LokiJS.git#d08f660794be558529f5ba049fe2868d4f243ec4" +"lokijs@git+https://github.com/Nozbe/LokiJS.git#fabe503": + version "1.5.9" + resolved "git+https://github.com/Nozbe/LokiJS.git#fabe503e4fc7887b76b0e4892b28fbd0753da27d" lolex@^5.0.0: version "5.1.2" @@ -12573,6 +12579,11 @@ react-fast-compare@^3.0.1: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.1.1.tgz#0becf31e3812fa70dc231e259f40d892d4767900" integrity sha512-SCsAORWK59BvauR2L1BTdjQbJcSGJJz03U0awektk2hshLKrITDDFTlgGCqIZpTDlPC/NFlZee6xTMzXPVLiHw== +react-fast-compare@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + react-focus-lock@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.3.1.tgz#9d5d85899773609c7eefa4fc54fff6a0f5f2fc47"