[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:
parent
9882ace694
commit
9dbe10bcf8
|
@ -4994,137 +4994,6 @@ exports[`Storyshots Message list message 1`] = `
|
||||||
</RCTScrollView>
|
</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`] = `
|
exports[`Storyshots UiKitMessage list uikitmessage 1`] = `
|
||||||
<RCTSafeAreaView
|
<RCTSafeAreaView
|
||||||
emulateUnlessSupported={true}
|
emulateUnlessSupported={true}
|
||||||
|
|
|
@ -845,6 +845,12 @@ const RocketChat = {
|
||||||
return other && other.length ? other[0] : me;
|
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) {
|
isGroupChat(room) {
|
||||||
return (room.uids && room.uids.length > 2) || (room.usernames && room.usernames.length > 2);
|
return (room.uids && room.uids.length > 2) || (room.usernames && room.usernames.length > 2);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, Text } from 'react-native';
|
import { View, Text } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -16,82 +16,79 @@ import { themes } from '../../constants/colors';
|
||||||
export { ROW_HEIGHT };
|
export { ROW_HEIGHT };
|
||||||
|
|
||||||
const attrs = [
|
const attrs = [
|
||||||
'name',
|
|
||||||
'unread',
|
|
||||||
'userMentions',
|
|
||||||
'showLastMessage',
|
|
||||||
'useRealName',
|
|
||||||
'alert',
|
|
||||||
'type',
|
|
||||||
'width',
|
'width',
|
||||||
'isRead',
|
|
||||||
'favorite',
|
|
||||||
'status',
|
'status',
|
||||||
'connected',
|
'connected',
|
||||||
'theme',
|
'theme',
|
||||||
'isFocused'
|
'isFocused',
|
||||||
|
'forceUpdate',
|
||||||
|
'showLastMessage'
|
||||||
];
|
];
|
||||||
|
|
||||||
const arePropsEqual = (oldProps, newProps) => {
|
const arePropsEqual = (oldProps, newProps) => attrs.every(key => oldProps[key] === newProps[key]);
|
||||||
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 RoomItem = React.memo(({
|
const RoomItem = React.memo(({
|
||||||
|
item,
|
||||||
onPress,
|
onPress,
|
||||||
width,
|
width,
|
||||||
favorite,
|
|
||||||
toggleFav,
|
toggleFav,
|
||||||
isRead,
|
|
||||||
rid,
|
|
||||||
toggleRead,
|
toggleRead,
|
||||||
hideChannel,
|
hideChannel,
|
||||||
testID,
|
testID,
|
||||||
unread,
|
|
||||||
userMentions,
|
|
||||||
name,
|
|
||||||
_updatedAt,
|
|
||||||
alert,
|
|
||||||
type,
|
|
||||||
avatarSize,
|
avatarSize,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
userId,
|
userId,
|
||||||
username,
|
username,
|
||||||
token,
|
token,
|
||||||
id,
|
id,
|
||||||
prid,
|
|
||||||
showLastMessage,
|
showLastMessage,
|
||||||
hideUnreadStatus,
|
|
||||||
lastMessage,
|
|
||||||
status,
|
status,
|
||||||
avatar,
|
|
||||||
useRealName,
|
useRealName,
|
||||||
getUserPresence,
|
getUserPresence,
|
||||||
isGroupChat,
|
|
||||||
connected,
|
connected,
|
||||||
theme,
|
theme,
|
||||||
isFocused
|
isFocused,
|
||||||
|
getRoomTitle,
|
||||||
|
getRoomAvatar,
|
||||||
|
getIsGroupChat,
|
||||||
|
getIsRead
|
||||||
}) => {
|
}) => {
|
||||||
|
const [, setForceUpdate] = useState(1);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (connected && type === 'd' && id) {
|
if (connected && item.t === 'd' && id) {
|
||||||
getUserPresence(id);
|
getUserPresence(id);
|
||||||
}
|
}
|
||||||
}, [connected]);
|
}, [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;
|
let accessibilityLabel = name;
|
||||||
if (unread === 1) {
|
if (item.unread === 1) {
|
||||||
accessibilityLabel += `, ${ unread } ${ I18n.t('alert') }`;
|
accessibilityLabel += `, ${ item.unread } ${ I18n.t('alert') }`;
|
||||||
} else if (unread > 1) {
|
} else if (item.unread > 1) {
|
||||||
accessibilityLabel += `, ${ unread } ${ I18n.t('alerts') }`;
|
accessibilityLabel += `, ${ item.unread } ${ I18n.t('alerts') }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userMentions > 0) {
|
if (item.userMentions > 0) {
|
||||||
accessibilityLabel += `, ${ I18n.t('you_were_mentioned') }`;
|
accessibilityLabel += `, ${ I18n.t('you_were_mentioned') }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,16 +98,16 @@ const RoomItem = React.memo(({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Touchable
|
<Touchable
|
||||||
onPress={onPress}
|
onPress={_onPress}
|
||||||
width={width}
|
width={width}
|
||||||
favorite={favorite}
|
favorite={item.f}
|
||||||
toggleFav={toggleFav}
|
toggleFav={toggleFav}
|
||||||
isRead={isRead}
|
isRead={isRead}
|
||||||
rid={rid}
|
rid={item.rid}
|
||||||
toggleRead={toggleRead}
|
toggleRead={toggleRead}
|
||||||
hideChannel={hideChannel}
|
hideChannel={hideChannel}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
type={type}
|
type={item.t}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
isFocused={isFocused}
|
isFocused={isFocused}
|
||||||
>
|
>
|
||||||
|
@ -121,7 +118,7 @@ const RoomItem = React.memo(({
|
||||||
<Avatar
|
<Avatar
|
||||||
text={avatar}
|
text={avatar}
|
||||||
size={avatarSize}
|
size={avatarSize}
|
||||||
type={type}
|
type={item.t}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
style={styles.avatar}
|
style={styles.avatar}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
|
@ -137,8 +134,8 @@ const RoomItem = React.memo(({
|
||||||
>
|
>
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
<TypeIcon
|
<TypeIcon
|
||||||
type={type}
|
type={item.t}
|
||||||
prid={prid}
|
prid={item.prid}
|
||||||
status={status}
|
status={status}
|
||||||
isGroupChat={isGroupChat}
|
isGroupChat={isGroupChat}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
@ -146,7 +143,7 @@ const RoomItem = React.memo(({
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.title,
|
styles.title,
|
||||||
alert && !hideUnreadStatus && styles.alert,
|
item.alert && !item.hideUnreadStatus && styles.alert,
|
||||||
{ color: themes[theme].titleText }
|
{ color: themes[theme].titleText }
|
||||||
]}
|
]}
|
||||||
ellipsizeMode='tail'
|
ellipsizeMode='tail'
|
||||||
|
@ -154,7 +151,7 @@ const RoomItem = React.memo(({
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
{_updatedAt ? (
|
{item.roomUpdatedAt ? (
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.date,
|
styles.date,
|
||||||
|
@ -163,7 +160,7 @@ const RoomItem = React.memo(({
|
||||||
themes[theme]
|
themes[theme]
|
||||||
.auxiliaryText
|
.auxiliaryText
|
||||||
},
|
},
|
||||||
alert && !hideUnreadStatus && [
|
item.alert && !item.hideUnreadStatus && [
|
||||||
styles.updateAlert,
|
styles.updateAlert,
|
||||||
{
|
{
|
||||||
color:
|
color:
|
||||||
|
@ -181,18 +178,18 @@ const RoomItem = React.memo(({
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
<LastMessage
|
<LastMessage
|
||||||
lastMessage={lastMessage}
|
lastMessage={item.lastMessage}
|
||||||
type={type}
|
type={item.t}
|
||||||
showLastMessage={showLastMessage}
|
showLastMessage={showLastMessage}
|
||||||
username={username}
|
username={username}
|
||||||
alert={alert && !hideUnreadStatus}
|
alert={item.alert && !item.hideUnreadStatus}
|
||||||
useRealName={useRealName}
|
useRealName={useRealName}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
<UnreadBadge
|
<UnreadBadge
|
||||||
unread={unread}
|
unread={item.unread}
|
||||||
userMentions={userMentions}
|
userMentions={item.userMentions}
|
||||||
type={type}
|
type={item.t}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
@ -203,17 +200,10 @@ const RoomItem = React.memo(({
|
||||||
}, arePropsEqual);
|
}, arePropsEqual);
|
||||||
|
|
||||||
RoomItem.propTypes = {
|
RoomItem.propTypes = {
|
||||||
type: PropTypes.string.isRequired,
|
item: PropTypes.object.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
baseUrl: PropTypes.string.isRequired,
|
baseUrl: PropTypes.string.isRequired,
|
||||||
showLastMessage: PropTypes.bool,
|
showLastMessage: PropTypes.bool,
|
||||||
_updatedAt: PropTypes.string,
|
|
||||||
lastMessage: PropTypes.object,
|
|
||||||
alert: PropTypes.bool,
|
|
||||||
unread: PropTypes.number,
|
|
||||||
userMentions: PropTypes.number,
|
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
prid: PropTypes.string,
|
|
||||||
onPress: PropTypes.func,
|
onPress: PropTypes.func,
|
||||||
userId: PropTypes.string,
|
userId: PropTypes.string,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
|
@ -221,27 +211,29 @@ RoomItem.propTypes = {
|
||||||
avatarSize: PropTypes.number,
|
avatarSize: PropTypes.number,
|
||||||
testID: PropTypes.string,
|
testID: PropTypes.string,
|
||||||
width: PropTypes.number,
|
width: PropTypes.number,
|
||||||
favorite: PropTypes.bool,
|
|
||||||
isRead: PropTypes.bool,
|
|
||||||
rid: PropTypes.string,
|
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
toggleFav: PropTypes.func,
|
toggleFav: PropTypes.func,
|
||||||
toggleRead: PropTypes.func,
|
toggleRead: PropTypes.func,
|
||||||
hideChannel: PropTypes.func,
|
hideChannel: PropTypes.func,
|
||||||
avatar: PropTypes.bool,
|
|
||||||
hideUnreadStatus: PropTypes.bool,
|
|
||||||
useRealName: PropTypes.bool,
|
useRealName: PropTypes.bool,
|
||||||
getUserPresence: PropTypes.func,
|
getUserPresence: PropTypes.func,
|
||||||
connected: PropTypes.bool,
|
connected: PropTypes.bool,
|
||||||
isGroupChat: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
isFocused: PropTypes.bool
|
isFocused: PropTypes.bool,
|
||||||
|
getRoomTitle: PropTypes.func,
|
||||||
|
getRoomAvatar: PropTypes.func,
|
||||||
|
getIsGroupChat: PropTypes.func,
|
||||||
|
getIsRead: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
RoomItem.defaultProps = {
|
RoomItem.defaultProps = {
|
||||||
avatarSize: 48,
|
avatarSize: 48,
|
||||||
status: 'offline',
|
status: 'offline',
|
||||||
getUserPresence: () => {}
|
getUserPresence: () => {},
|
||||||
|
getRoomTitle: () => 'title',
|
||||||
|
getRoomAvatar: () => '',
|
||||||
|
getIsGroupChat: () => false,
|
||||||
|
getIsRead: () => false
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FlatList, RefreshControl } from 'react-native';
|
import { FlatList, RefreshControl } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import orderBy from 'lodash/orderBy';
|
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
|
@ -15,9 +14,10 @@ import EmptyRoom from './EmptyRoom';
|
||||||
import { isIOS } from '../../utils/deviceInfo';
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
import { animateNextTransition } from '../../utils/layoutAnimation';
|
import { animateNextTransition } from '../../utils/layoutAnimation';
|
||||||
import ActivityIndicator from '../../containers/ActivityIndicator';
|
import ActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
import debounce from '../../utils/debounce';
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
|
||||||
|
const QUERY_SIZE = 50;
|
||||||
|
|
||||||
class List extends React.Component {
|
class List extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onEndReached: PropTypes.func,
|
onEndReached: PropTypes.func,
|
||||||
|
@ -47,7 +47,8 @@ class List extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
console.time(`${ this.constructor.name } init`);
|
console.time(`${ this.constructor.name } init`);
|
||||||
console.time(`${ this.constructor.name } mount`);
|
console.time(`${ this.constructor.name } mount`);
|
||||||
|
this.count = 0;
|
||||||
|
this.needsFetch = false;
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
|
@ -56,7 +57,7 @@ class List extends React.Component {
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
animated: false
|
animated: false
|
||||||
};
|
};
|
||||||
this.init();
|
this.query();
|
||||||
this.unsubscribeFocus = props.navigation.addListener('focus', () => {
|
this.unsubscribeFocus = props.navigation.addListener('focus', () => {
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ animated: true });
|
this.setState({ animated: true });
|
||||||
|
@ -72,72 +73,6 @@ class List extends React.Component {
|
||||||
console.timeEnd(`${ this.constructor.name } mount`);
|
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) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
const { loading, end, refreshing } = this.state;
|
const { loading, end, refreshing } = this.state;
|
||||||
const { hideSystemMessages, theme } = this.props;
|
const { hideSystemMessages, theme } = this.props;
|
||||||
|
@ -177,7 +112,7 @@ class List extends React.Component {
|
||||||
console.countReset(`${ this.constructor.name }.render calls`);
|
console.countReset(`${ this.constructor.name }.render calls`);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEndReached = debounce(async() => {
|
fetchData = async() => {
|
||||||
const {
|
const {
|
||||||
loading, end, messages, latest = messages[messages.length - 1]?.ts
|
loading, end, messages, latest = messages[messages.length - 1]?.ts
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
@ -196,12 +131,99 @@ class List extends React.Component {
|
||||||
result = await RocketChat.loadMessagesForRoom({ rid, t, latest });
|
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) {
|
} catch (e) {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
log(e);
|
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) => {
|
loadMoreMessages = (result) => {
|
||||||
const { end } = this.state;
|
const { end } = this.state;
|
||||||
|
@ -305,7 +327,7 @@ class List extends React.Component {
|
||||||
removeClippedSubviews={isIOS}
|
removeClippedSubviews={isIOS}
|
||||||
initialNumToRender={7}
|
initialNumToRender={7}
|
||||||
onEndReached={this.onEndReached}
|
onEndReached={this.onEndReached}
|
||||||
onEndReachedThreshold={5}
|
onEndReachedThreshold={0.5}
|
||||||
maxToRenderPerBatch={5}
|
maxToRenderPerBatch={5}
|
||||||
windowSize={10}
|
windowSize={10}
|
||||||
ListFooterComponent={this.renderFooter}
|
ListFooterComponent={this.renderFooter}
|
||||||
|
|
|
@ -206,12 +206,10 @@ class RoomView extends React.Component {
|
||||||
const { appState, insets } = this.props;
|
const { appState, insets } = this.props;
|
||||||
|
|
||||||
if (appState === 'foreground' && appState !== prevProps.appState && this.rid) {
|
if (appState === 'foreground' && appState !== prevProps.appState && this.rid) {
|
||||||
this.onForegroundInteraction = InteractionManager.runAfterInteractions(() => {
|
// Fire List.query() just to keep observables working
|
||||||
// Fire List.init() just to keep observables working
|
|
||||||
if (this.list && this.list.current) {
|
if (this.list && this.list.current) {
|
||||||
this.list.current.init();
|
this.list.current?.query?.();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// If it's not direct message
|
// If it's not direct message
|
||||||
if (this.t !== 'd') {
|
if (this.t !== 'd') {
|
||||||
|
@ -267,9 +265,6 @@ class RoomView extends React.Component {
|
||||||
if (this.didMountInteraction && this.didMountInteraction.cancel) {
|
if (this.didMountInteraction && this.didMountInteraction.cancel) {
|
||||||
this.didMountInteraction.cancel();
|
this.didMountInteraction.cancel();
|
||||||
}
|
}
|
||||||
if (this.onForegroundInteraction && this.onForegroundInteraction.cancel) {
|
|
||||||
this.onForegroundInteraction.cancel();
|
|
||||||
}
|
|
||||||
if (this.willBlurListener && this.willBlurListener.remove) {
|
if (this.willBlurListener && this.willBlurListener.remove) {
|
||||||
this.willBlurListener.remove();
|
this.willBlurListener.remove();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
RefreshControl
|
RefreshControl
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { isEqual, orderBy } from 'lodash';
|
import isEqual from 'react-fast-compare';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
import Orientation from 'react-native-orientation-locker';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
@ -71,6 +71,7 @@ const DISCUSSIONS_HEADER = 'Discussions';
|
||||||
const CHANNELS_HEADER = 'Channels';
|
const CHANNELS_HEADER = 'Channels';
|
||||||
const DM_HEADER = 'Direct_Messages';
|
const DM_HEADER = 'Direct_Messages';
|
||||||
const GROUPS_HEADER = 'Private_Groups';
|
const GROUPS_HEADER = 'Private_Groups';
|
||||||
|
const QUERY_SIZE = 20;
|
||||||
|
|
||||||
const filterIsUnread = s => (s.unread > 0 || s.alert) && !s.hideUnreadStatus;
|
const filterIsUnread = s => (s.unread > 0 || s.alert) && !s.hideUnreadStatus;
|
||||||
const filterIsFavorite = s => s.f;
|
const filterIsFavorite = s => s.f;
|
||||||
|
@ -140,11 +141,12 @@ class RoomsListView extends React.Component {
|
||||||
|
|
||||||
this.gotSubscriptions = false;
|
this.gotSubscriptions = false;
|
||||||
this.animated = false;
|
this.animated = false;
|
||||||
|
this.count = 0;
|
||||||
this.state = {
|
this.state = {
|
||||||
searching: false,
|
searching: false,
|
||||||
search: [],
|
search: [],
|
||||||
loading: true,
|
loading: true,
|
||||||
allChats: [],
|
chatsOrder: [],
|
||||||
chats: [],
|
chats: [],
|
||||||
item: {}
|
item: {}
|
||||||
};
|
};
|
||||||
|
@ -211,7 +213,7 @@ class RoomsListView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
const { allChats, searching, item } = this.state;
|
const { chatsOrder, searching, item } = this.state;
|
||||||
// eslint-disable-next-line react/destructuring-assignment
|
// eslint-disable-next-line react/destructuring-assignment
|
||||||
const propsUpdated = shouldUpdateProps.some(key => nextProps[key] !== this.props[key]);
|
const propsUpdated = shouldUpdateProps.some(key => nextProps[key] !== this.props[key]);
|
||||||
if (propsUpdated) {
|
if (propsUpdated) {
|
||||||
|
@ -219,7 +221,7 @@ class RoomsListView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare changes only once
|
// 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 they aren't equal, set to update if focused
|
||||||
if (chatsNotEqual) {
|
if (chatsNotEqual) {
|
||||||
|
@ -290,7 +292,7 @@ class RoomsListView extends React.Component {
|
||||||
&& prevProps.showUnread === showUnread
|
&& prevProps.showUnread === showUnread
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
this.getSubscriptions(true);
|
this.getSubscriptions();
|
||||||
} else if (
|
} else if (
|
||||||
appState === 'foreground'
|
appState === 'foreground'
|
||||||
&& appState !== prevProps.appState
|
&& appState !== prevProps.appState
|
||||||
|
@ -309,9 +311,7 @@ class RoomsListView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.querySubscription && this.querySubscription.unsubscribe) {
|
this.unsubscribeQuery();
|
||||||
this.querySubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
if (this.unsubscribeFocus) {
|
if (this.unsubscribeFocus) {
|
||||||
this.unsubscribeFocus();
|
this.unsubscribeFocus();
|
||||||
}
|
}
|
||||||
|
@ -396,17 +396,8 @@ class RoomsListView extends React.Component {
|
||||||
return allData;
|
return allData;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSubscriptions = async(force = false) => {
|
getSubscriptions = async() => {
|
||||||
if (this.gotSubscriptions && !force) {
|
this.unsubscribeQuery();
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.gotSubscriptions = true;
|
|
||||||
|
|
||||||
if (this.querySubscription && this.querySubscription.unsubscribe) {
|
|
||||||
this.querySubscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ loading: true });
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
sortBy,
|
sortBy,
|
||||||
|
@ -416,41 +407,49 @@ class RoomsListView extends React.Component {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const observable = await db.collections
|
let observable;
|
||||||
.get('subscriptions')
|
|
||||||
.query(
|
const defaultWhereClause = [
|
||||||
Q.where('archived', false),
|
Q.where('archived', false),
|
||||||
Q.where('open', true)
|
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)
|
||||||
)
|
)
|
||||||
.observeWithColumns(['room_updated_at', 'unread', 'alert', 'user_mentions', 'f', 't']);
|
.observe();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.querySubscription = observable.subscribe((data) => {
|
this.querySubscription = observable.subscribe((data) => {
|
||||||
let tempChats = [];
|
let tempChats = [];
|
||||||
let chats = [];
|
let chats = data;
|
||||||
if (sortBy === 'alphabetical') {
|
|
||||||
chats = orderBy(data, [`${ this.useRealName ? 'fname' : 'name' }`], ['asc']);
|
|
||||||
} else {
|
|
||||||
chats = orderBy(data, ['roomUpdatedAt'], ['desc']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// it's better to map and test all subs altogether then testing them individually
|
/**
|
||||||
const allChats = data.map(item => ({
|
* We trigger re-render only when chats order changes
|
||||||
alert: item.alert,
|
* RoomItem handles its own re-render
|
||||||
unread: item.unread,
|
*/
|
||||||
userMentions: item.userMentions,
|
const chatsOrder = data.map(item => item.rid);
|
||||||
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
|
|
||||||
}));
|
|
||||||
|
|
||||||
// unread
|
// unread
|
||||||
if (showUnread) {
|
if (showUnread) {
|
||||||
|
@ -484,12 +483,18 @@ class RoomsListView extends React.Component {
|
||||||
|
|
||||||
this.internalSetState({
|
this.internalSetState({
|
||||||
chats: tempChats,
|
chats: tempChats,
|
||||||
allChats,
|
chatsOrder,
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsubscribeQuery = () => {
|
||||||
|
if (this.querySubscription && this.querySubscription.unsubscribe) {
|
||||||
|
this.querySubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initSearching = () => {
|
initSearching = () => {
|
||||||
const { openSearchHeader } = this.props;
|
const { openSearchHeader } = this.props;
|
||||||
this.internalSetState({ searching: true }, () => {
|
this.internalSetState({ searching: true }, () => {
|
||||||
|
@ -548,10 +553,19 @@ class RoomsListView extends React.Component {
|
||||||
|
|
||||||
getRoomAvatar = item => RocketChat.getRoomAvatar(item)
|
getRoomAvatar = item => RocketChat.getRoomAvatar(item)
|
||||||
|
|
||||||
|
isGroupChat = item => RocketChat.isGroupChat(item)
|
||||||
|
|
||||||
|
isRead = item => RocketChat.isRead(item)
|
||||||
|
|
||||||
getUserPresence = uid => RocketChat.getUserPresence(uid)
|
getUserPresence = uid => RocketChat.getUserPresence(uid)
|
||||||
|
|
||||||
getUidDirectMessage = room => RocketChat.getUidDirectMessage(room);
|
getUidDirectMessage = room => RocketChat.getUidDirectMessage(room);
|
||||||
|
|
||||||
|
get isGrouping() {
|
||||||
|
const { showUnread, showFavorites, groupByType } = this.props;
|
||||||
|
return showUnread || showFavorites || groupByType;
|
||||||
|
}
|
||||||
|
|
||||||
onPressItem = (item = {}) => {
|
onPressItem = (item = {}) => {
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { navigation, isMasterDetail } = this.props;
|
||||||
if (!navigation.isFocused()) {
|
if (!navigation.isFocused()) {
|
||||||
|
@ -743,6 +757,13 @@ class RoomsListView extends React.Component {
|
||||||
roomsRequest({ allData: true });
|
roomsRequest({ allData: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEndReached = () => {
|
||||||
|
// Run only when we're not grouping by anything
|
||||||
|
if (!this.isGrouping) {
|
||||||
|
this.getSubscriptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getScrollRef = ref => (this.scroll = ref);
|
getScrollRef = ref => (this.scroll = ref);
|
||||||
|
|
||||||
renderListHeader = () => {
|
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 }) => {
|
renderItem = ({ item }) => {
|
||||||
if (item.separator) {
|
if (item.separator) {
|
||||||
return this.renderSectionHeader(item.rid);
|
return this.renderSectionHeader(item.rid);
|
||||||
|
@ -800,32 +815,19 @@ class RoomsListView extends React.Component {
|
||||||
width
|
width
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const id = this.getUidDirectMessage(item);
|
const id = this.getUidDirectMessage(item);
|
||||||
const isGroupChat = RocketChat.isGroupChat(item);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RoomItem
|
<RoomItem
|
||||||
|
item={item}
|
||||||
theme={theme}
|
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}
|
id={id}
|
||||||
|
type={item.t}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
username={username}
|
username={username}
|
||||||
token={token}
|
token={token}
|
||||||
rid={item.rid}
|
|
||||||
type={item.t}
|
|
||||||
baseUrl={server}
|
baseUrl={server}
|
||||||
prid={item.prid}
|
|
||||||
showLastMessage={StoreLastMessage}
|
showLastMessage={StoreLastMessage}
|
||||||
onPress={() => this.onPressItem(item)}
|
onPress={this.onPressItem}
|
||||||
testID={`rooms-list-view-item-${ item.name }`}
|
testID={`rooms-list-view-item-${ item.name }`}
|
||||||
width={isMasterDetail ? MAX_SIDEBAR_WIDTH : width}
|
width={isMasterDetail ? MAX_SIDEBAR_WIDTH : width}
|
||||||
toggleFav={this.toggleFav}
|
toggleFav={this.toggleFav}
|
||||||
|
@ -833,7 +835,10 @@ class RoomsListView extends React.Component {
|
||||||
hideChannel={this.hideChannel}
|
hideChannel={this.hideChannel}
|
||||||
useRealName={useRealName}
|
useRealName={useRealName}
|
||||||
getUserPresence={this.getUserPresence}
|
getUserPresence={this.getUserPresence}
|
||||||
isGroupChat={isGroupChat}
|
getRoomTitle={this.getRoomTitle}
|
||||||
|
getRoomAvatar={this.getRoomAvatar}
|
||||||
|
getIsGroupChat={this.isGroupChat}
|
||||||
|
getIsRead={this.isRead}
|
||||||
visitor={item.visitor}
|
visitor={item.visitor}
|
||||||
isFocused={currentItem?.rid === item.rid}
|
isFocused={currentItem?.rid === item.rid}
|
||||||
/>
|
/>
|
||||||
|
@ -880,6 +885,8 @@ class RoomsListView extends React.Component {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
windowSize={9}
|
windowSize={9}
|
||||||
|
onEndReached={this.onEndReached}
|
||||||
|
onEndReachedThreshold={0.5}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ import ShareExtension from 'rn-extensions-share';
|
||||||
import * as FileSystem from 'expo-file-system';
|
import * as FileSystem from 'expo-file-system';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import * as mime from 'react-native-mime-types';
|
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 { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
|
@ -32,7 +32,6 @@ const permission = {
|
||||||
message: I18n.t('Read_External_Permission_Message')
|
message: I18n.t('Read_External_Permission_Message')
|
||||||
};
|
};
|
||||||
|
|
||||||
const LIMIT = 50;
|
|
||||||
const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index });
|
const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index });
|
||||||
const keyExtractor = item => item.rid;
|
const keyExtractor = item => item.rid;
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ class ShareListView extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.data = [];
|
this.chats = [];
|
||||||
this.state = {
|
this.state = {
|
||||||
searching: false,
|
searching: false,
|
||||||
searchText: '',
|
searchText: '',
|
||||||
|
@ -186,22 +185,36 @@ class ShareListView extends React.Component {
|
||||||
this.setState(...args);
|
this.setState(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSubscriptions = async(server) => {
|
query = (text) => {
|
||||||
const db = database.active;
|
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;
|
const serversDB = database.servers;
|
||||||
|
|
||||||
if (server) {
|
if (server) {
|
||||||
this.data = await db.collections
|
this.chats = await this.query();
|
||||||
.get('subscriptions')
|
|
||||||
.query(
|
|
||||||
Q.where('archived', false),
|
|
||||||
Q.where('open', true)
|
|
||||||
).fetch();
|
|
||||||
this.data = orderBy(this.data, ['roomUpdatedAt'], ['desc']);
|
|
||||||
|
|
||||||
const serversCollection = serversDB.collections.get('servers');
|
const serversCollection = serversDB.collections.get('servers');
|
||||||
this.servers = await serversCollection.query().fetch();
|
this.servers = await serversCollection.query().fetch();
|
||||||
this.chats = this.data.slice(0, LIMIT);
|
|
||||||
let serverInfo = {};
|
let serverInfo = {};
|
||||||
try {
|
try {
|
||||||
serverInfo = await serversCollection.find(server);
|
serverInfo = await serversCollection.find(server);
|
||||||
|
@ -210,8 +223,8 @@ class ShareListView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.internalSetState({
|
this.internalSetState({
|
||||||
chats: this.chats ? this.chats.slice() : [],
|
chats: this.chats ?? [],
|
||||||
servers: this.servers ? this.servers.slice() : [],
|
servers: this.servers ?? [],
|
||||||
loading: false,
|
loading: false,
|
||||||
serverInfo
|
serverInfo
|
||||||
});
|
});
|
||||||
|
@ -253,10 +266,10 @@ class ShareListView extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
search = (text) => {
|
search = async(text) => {
|
||||||
const result = this.data.filter(item => item.name.includes(text)) || [];
|
const result = await this.query(text);
|
||||||
this.internalSetState({
|
this.internalSetState({
|
||||||
searchResults: result.slice(0, LIMIT),
|
searchResults: result,
|
||||||
searchText: text
|
searchText: text
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -297,9 +310,26 @@ class ShareListView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem = ({ item }) => {
|
renderItem = ({ item }) => {
|
||||||
|
const { serverInfo } = this.state;
|
||||||
|
const { useRealName } = serverInfo;
|
||||||
const {
|
const {
|
||||||
userId, token, server, theme
|
userId, token, server, theme
|
||||||
} = this.props;
|
} = 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 (
|
return (
|
||||||
<DirectoryItem
|
<DirectoryItem
|
||||||
user={{
|
user={{
|
||||||
|
@ -309,11 +339,7 @@ class ShareListView extends React.Component {
|
||||||
title={this.getRoomTitle(item)}
|
title={this.getRoomTitle(item)}
|
||||||
baseUrl={server}
|
baseUrl={server}
|
||||||
avatar={RocketChat.getRoomAvatar(item)}
|
avatar={RocketChat.getRoomAvatar(item)}
|
||||||
description={
|
description={description}
|
||||||
item.t === 'c'
|
|
||||||
? (item.topic || item.description)
|
|
||||||
: item.fname
|
|
||||||
}
|
|
||||||
type={item.prid ? 'discussion' : item.t}
|
type={item.prid ? 'discussion' : item.t}
|
||||||
onPress={() => this.shareMessage(item)}
|
onPress={() => this.shareMessage(item)}
|
||||||
testID={`share-extension-item-${ item.name }`}
|
testID={`share-extension-item-${ item.name }`}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codler/react-native-keyboard-aware-scroll-view": "^1.0.1",
|
"@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/art": "^1.2.0",
|
||||||
"@react-native-community/async-storage": "1.11.0",
|
"@react-native-community/async-storage": "1.11.0",
|
||||||
"@react-native-community/cameraroll": "4.0.0",
|
"@react-native-community/cameraroll": "4.0.0",
|
||||||
|
@ -61,6 +61,7 @@
|
||||||
"pretty-bytes": "^5.3.0",
|
"pretty-bytes": "^5.3.0",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "15.7.2",
|
||||||
"react": "16.13.1",
|
"react": "16.13.1",
|
||||||
|
"react-fast-compare": "^3.2.0",
|
||||||
"react-native": "^0.63.1",
|
"react-native": "^0.63.1",
|
||||||
"react-native-animatable": "^1.3.3",
|
"react-native-animatable": "^1.3.3",
|
||||||
"react-native-appearance": "0.3.4",
|
"react-native-appearance": "0.3.4",
|
||||||
|
|
|
@ -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 = (
|
|
|
@ -1,10 +1,10 @@
|
||||||
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Provider } from 'react-redux';
|
// import { Provider } from 'react-redux';
|
||||||
import { createStore, combineReducers } from 'redux';
|
// import { createStore, combineReducers } from 'redux';
|
||||||
import { storiesOf } from '@storybook/react-native';
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
|
||||||
import RoomItem from './RoomItem';
|
// import RoomItem from './RoomItem';
|
||||||
import Message from './Message';
|
import Message from './Message';
|
||||||
import UiKitMessage from './UiKitMessage';
|
import UiKitMessage from './UiKitMessage';
|
||||||
import UiKitModal from './UiKitModal';
|
import UiKitModal from './UiKitModal';
|
||||||
|
@ -24,17 +24,17 @@ const user = {
|
||||||
// Change here to see themed storybook
|
// Change here to see themed storybook
|
||||||
const theme = 'light';
|
const theme = 'light';
|
||||||
|
|
||||||
const reducers = combineReducers({
|
// const reducers = combineReducers({
|
||||||
settings: () => ({}),
|
// settings: () => ({}),
|
||||||
login: () => ({
|
// login: () => ({
|
||||||
user: {
|
// user: {
|
||||||
username: 'diego.mello'
|
// username: 'diego.mello'
|
||||||
}
|
// }
|
||||||
}),
|
// }),
|
||||||
meteor: () => ({ connected: true }),
|
// meteor: () => ({ connected: true }),
|
||||||
activeUsers: () => ({ abc: { status: 'online', statusText: 'dog' } })
|
// activeUsers: () => ({ abc: { status: 'online', statusText: 'dog' } })
|
||||||
});
|
// });
|
||||||
const store = createStore(reducers);
|
// const store = createStore(reducers);
|
||||||
|
|
||||||
const messageDecorator = story => (
|
const messageDecorator = story => (
|
||||||
<MessageContext.Provider
|
<MessageContext.Provider
|
||||||
|
@ -55,9 +55,9 @@ const messageDecorator = story => (
|
||||||
</MessageContext.Provider>
|
</MessageContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
storiesOf('RoomItem', module)
|
// storiesOf('RoomItem', module)
|
||||||
.addDecorator(story => <Provider store={store}>{story()}</Provider>)
|
// .addDecorator(story => <Provider store={store}>{story()}</Provider>)
|
||||||
.add('list roomitem', () => <RoomItem theme={theme} />);
|
// .add('list roomitem', () => <RoomItem theme={theme} />);
|
||||||
storiesOf('Message', module)
|
storiesOf('Message', module)
|
||||||
.addDecorator(messageDecorator)
|
.addDecorator(messageDecorator)
|
||||||
.add('list message', () => <Message theme={theme} />);
|
.add('list message', () => <Message theme={theme} />);
|
||||||
|
|
27
yarn.lock
27
yarn.lock
|
@ -1917,13 +1917,19 @@
|
||||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
|
||||||
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
|
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
|
||||||
|
|
||||||
"@nozbe/watermelondb@0.16.2":
|
"@nozbe/sqlite@3.31.1":
|
||||||
version "0.16.2"
|
version "3.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nozbe/watermelondb/-/watermelondb-0.16.2.tgz#9c29c426bedb3d3af5d38304344bb49d60ddd1af"
|
resolved "https://registry.yarnpkg.com/@nozbe/sqlite/-/sqlite-3.31.1.tgz#ffd394ad7c188c6b73f89fd6e1ccb849a1b96dba"
|
||||||
integrity sha512-LLOEl13bVCiLsZf8QCHt5KUmuks7QnMtsXuhBB2rSb1vBDjc3mG2HmkH/eTzbgJnGIdFzH/XtoVUHlS/ll7Log==
|
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:
|
dependencies:
|
||||||
|
"@nozbe/sqlite" "3.31.1"
|
||||||
lodash.clonedeep "^4.5.0"
|
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"
|
rambdax "2.15.0"
|
||||||
rxjs "^6.2.2"
|
rxjs "^6.2.2"
|
||||||
rxjs-compat "^6.3.2"
|
rxjs-compat "^6.3.2"
|
||||||
|
@ -10206,9 +10212,9 @@ logkitty@^0.7.1:
|
||||||
dayjs "^1.8.15"
|
dayjs "^1.8.15"
|
||||||
yargs "^15.1.0"
|
yargs "^15.1.0"
|
||||||
|
|
||||||
"lokijs@git+https://github.com/Nozbe/LokiJS.git#d08f660":
|
"lokijs@git+https://github.com/Nozbe/LokiJS.git#fabe503":
|
||||||
version "1.5.7"
|
version "1.5.9"
|
||||||
resolved "git+https://github.com/Nozbe/LokiJS.git#d08f660794be558529f5ba049fe2868d4f243ec4"
|
resolved "git+https://github.com/Nozbe/LokiJS.git#fabe503e4fc7887b76b0e4892b28fbd0753da27d"
|
||||||
|
|
||||||
lolex@^5.0.0:
|
lolex@^5.0.0:
|
||||||
version "5.1.2"
|
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"
|
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.1.1.tgz#0becf31e3812fa70dc231e259f40d892d4767900"
|
||||||
integrity sha512-SCsAORWK59BvauR2L1BTdjQbJcSGJJz03U0awektk2hshLKrITDDFTlgGCqIZpTDlPC/NFlZee6xTMzXPVLiHw==
|
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:
|
react-focus-lock@^2.1.0:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.3.1.tgz#9d5d85899773609c7eefa4fc54fff6a0f5f2fc47"
|
resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.3.1.tgz#9d5d85899773609c7eefa4fc54fff6a0f5f2fc47"
|
||||||
|
|
Loading…
Reference in New Issue