[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
This commit is contained in:
Diego Mello 2020-07-20 13:44:54 -03:00 committed by GitHub
parent 9882ace694
commit 9dbe10bcf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 339 additions and 430 deletions

View File

@ -4994,137 +4994,6 @@ exports[`Storyshots Message list message 1`] = `
</RCTScrollView>
`;
exports[`Storyshots RoomItem list roomitem 1`] = `
<RCTScrollView
style={
Object {
"backgroundColor": "#efeff4",
}
}
>
<View>
<Text
style={
Array [
Object {
"fontSize": 20,
"fontWeight": "300",
"marginLeft": 10,
"marginTop": 30,
},
Object {
"backgroundColor": "#ffffff",
"color": "#0d0e12",
},
undefined,
]
}
>
Basic
</Text>
View
<Text
style={
Array [
Object {
"fontSize": 20,
"fontWeight": "300",
"marginLeft": 10,
"marginTop": 30,
},
Object {
"backgroundColor": "#ffffff",
"color": "#0d0e12",
},
undefined,
]
}
>
User
</Text>
View
View
<Text
style={
Array [
Object {
"fontSize": 20,
"fontWeight": "300",
"marginLeft": 10,
"marginTop": 30,
},
Object {
"backgroundColor": "#ffffff",
"color": "#0d0e12",
},
undefined,
]
}
>
Type
</Text>
View
View
View
View
View
<Text
style={
Array [
Object {
"fontSize": 20,
"fontWeight": "300",
"marginLeft": 10,
"marginTop": 30,
},
Object {
"backgroundColor": "#ffffff",
"color": "#0d0e12",
},
undefined,
]
}
>
Alerts
</Text>
View
View
View
View
View
View
View
View
View
<Text
style={
Array [
Object {
"fontSize": 20,
"fontWeight": "300",
"marginLeft": 10,
"marginTop": 30,
},
Object {
"backgroundColor": "#ffffff",
"color": "#0d0e12",
},
undefined,
]
}
>
Last Message
</Text>
View
View
View
View
View
View
</View>
</RCTScrollView>
`;
exports[`Storyshots UiKitMessage list uikitmessage 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}

View File

@ -845,6 +845,12 @@ const RocketChat = {
return other && other.length ? other[0] : me;
},
isRead(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;
},
isGroupChat(room) {
return (room.uids && room.uids.length > 2) || (room.usernames && room.usernames.length > 2);
},

View File

@ -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 (
<Touchable
onPress={onPress}
onPress={_onPress}
width={width}
favorite={favorite}
favorite={item.f}
toggleFav={toggleFav}
isRead={isRead}
rid={rid}
rid={item.rid}
toggleRead={toggleRead}
hideChannel={hideChannel}
testID={testID}
type={type}
type={item.t}
theme={theme}
isFocused={isFocused}
>
@ -121,7 +118,7 @@ const RoomItem = React.memo(({
<Avatar
text={avatar}
size={avatarSize}
type={type}
type={item.t}
baseUrl={baseUrl}
style={styles.avatar}
userId={userId}
@ -137,8 +134,8 @@ const RoomItem = React.memo(({
>
<View style={styles.titleContainer}>
<TypeIcon
type={type}
prid={prid}
type={item.t}
prid={item.prid}
status={status}
isGroupChat={isGroupChat}
theme={theme}
@ -146,7 +143,7 @@ const RoomItem = React.memo(({
<Text
style={[
styles.title,
alert && !hideUnreadStatus && styles.alert,
item.alert && !item.hideUnreadStatus && styles.alert,
{ color: themes[theme].titleText }
]}
ellipsizeMode='tail'
@ -154,7 +151,7 @@ const RoomItem = React.memo(({
>
{name}
</Text>
{_updatedAt ? (
{item.roomUpdatedAt ? (
<Text
style={[
styles.date,
@ -163,7 +160,7 @@ const RoomItem = React.memo(({
themes[theme]
.auxiliaryText
},
alert && !hideUnreadStatus && [
item.alert && !item.hideUnreadStatus && [
styles.updateAlert,
{
color:
@ -181,18 +178,18 @@ const RoomItem = React.memo(({
</View>
<View style={styles.row}>
<LastMessage
lastMessage={lastMessage}
type={type}
lastMessage={item.lastMessage}
type={item.t}
showLastMessage={showLastMessage}
username={username}
alert={alert && !hideUnreadStatus}
alert={item.alert && !item.hideUnreadStatus}
useRealName={useRealName}
theme={theme}
/>
<UnreadBadge
unread={unread}
userMentions={userMentions}
type={type}
unread={item.unread}
userMentions={item.userMentions}
type={item.t}
theme={theme}
/>
</View>
@ -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) => {

View File

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

View File

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

View File

@ -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 (
<RoomItem
item={item}
theme={theme}
alert={item.alert}
unread={item.unread}
hideUnreadStatus={item.hideUnreadStatus}
userMentions={item.userMentions}
isRead={this.getIsRead(item)}
favorite={item.f}
avatar={this.getRoomAvatar(item)}
lastMessage={item.lastMessage}
name={this.getRoomTitle(item)}
_updatedAt={item.roomUpdatedAt}
key={item._id}
id={id}
type={item.t}
userId={userId}
username={username}
token={token}
rid={item.rid}
type={item.t}
baseUrl={server}
prid={item.prid}
showLastMessage={StoreLastMessage}
onPress={() => 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}
/>
);
};

View File

@ -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 (
<DirectoryItem
user={{
@ -309,11 +339,7 @@ class ShareListView extends React.Component {
title={this.getRoomTitle(item)}
baseUrl={server}
avatar={RocketChat.getRoomAvatar(item)}
description={
item.t === 'c'
? (item.topic || item.description)
: item.fname
}
description={description}
type={item.prid ? 'discussion' : item.t}
onPress={() => this.shareMessage(item)}
testID={`share-extension-item-${ item.name }`}

View File

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

View File

@ -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 = (

View File

@ -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 => (
<MessageContext.Provider
@ -55,9 +55,9 @@ const messageDecorator = story => (
</MessageContext.Provider>
);
storiesOf('RoomItem', module)
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
.add('list roomitem', () => <RoomItem theme={theme} />);
// storiesOf('RoomItem', module)
// .addDecorator(story => <Provider store={store}>{story()}</Provider>)
// .add('list roomitem', () => <RoomItem theme={theme} />);
storiesOf('Message', module)
.addDecorator(messageDecorator)
.add('list message', () => <Message theme={theme} />);

View File

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