[IMPROVE] Add pagination to search messages (#3212)

* [FIX] Pagination in SearchMessage through the javascript

* Minor tweak

* Remove unnecessary state update

* Fix inconsistent value update

* Minor change

* Fixed searchMessages to work with new value of count

* minor tweak

* minor tweak

* minor tweak

* Fix encrypted search

* Added Offset to lib/rocketchat and fixed the search

* fixed the debounce in  search message view

* Needed to compare server version to lower than 3.17.0

Co-authored-by: Gerzon Z <gerzonc@icloud.com>
Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>
Co-authored-by: Levy Costa <levycosta471@gmail.com>
Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com>
This commit is contained in:
Reinaldo Neto 2021-10-05 10:58:21 -03:00 committed by GitHub
parent 62562ebba9
commit 2a19054bf6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 14 deletions

View File

@ -1537,11 +1537,13 @@ const RocketChat = {
messageId messageId
}); });
}, },
searchMessages(roomId, searchText) { searchMessages(roomId, searchText, count, offset) {
// RC 0.60.0 // RC 0.60.0
return this.sdk.get('chat.search', { return this.sdk.get('chat.search', {
roomId, roomId,
searchText searchText,
count,
offset
}); });
}, },
toggleFollowMessage(mid, follow) { toggleFollowMessage(mid, follow) {

View File

@ -20,7 +20,8 @@ class RightButtonsContainer extends Component {
navigation: PropTypes.object, navigation: PropTypes.object,
isMasterDetail: PropTypes.bool, isMasterDetail: PropTypes.bool,
toggleFollowThread: PropTypes.func, toggleFollowThread: PropTypes.func,
joined: PropTypes.bool joined: PropTypes.bool,
encrypted: PropTypes.bool
}; };
constructor(props) { constructor(props) {
@ -137,11 +138,14 @@ class RightButtonsContainer extends Component {
goSearchView = () => { goSearchView = () => {
logEvent(events.ROOM_GO_SEARCH); logEvent(events.ROOM_GO_SEARCH);
const { rid, t, navigation, isMasterDetail } = this.props; const { rid, t, navigation, isMasterDetail, encrypted } = this.props;
if (isMasterDetail) { if (isMasterDetail) {
navigation.navigate('ModalStackNavigator', { screen: 'SearchMessagesView', params: { rid, showCloseModal: true } }); navigation.navigate('ModalStackNavigator', {
screen: 'SearchMessagesView',
params: { rid, showCloseModal: true, encrypted }
});
} else { } else {
navigation.navigate('SearchMessagesView', { rid, t }); navigation.navigate('SearchMessagesView', { rid, t, encrypted });
} }
}; };

View File

@ -362,6 +362,7 @@ class RoomView extends React.Component {
const t = room?.t; const t = room?.t;
const teamMain = room?.teamMain; const teamMain = room?.teamMain;
const teamId = room?.teamId; const teamId = room?.teamId;
const encrypted = room?.encrypted;
const { id: userId, token } = user; const { id: userId, token } = user;
const avatar = room?.name; const avatar = room?.name;
const visitor = room?.visitor; const visitor = room?.visitor;
@ -424,6 +425,7 @@ class RoomView extends React.Component {
teamMain={teamMain} teamMain={teamMain}
joined={joined} joined={joined}
t={t} t={t}
encrypted={encrypted}
navigation={navigation} navigation={navigation}
toggleFollowThread={this.toggleFollowThread} toggleFollowThread={this.toggleFollowThread}
/> />

View File

@ -24,8 +24,11 @@ import database from '../../lib/database';
import { sanitizeLikeString } from '../../lib/database/utils'; import { sanitizeLikeString } from '../../lib/database/utils';
import getThreadName from '../../lib/methods/getThreadName'; import getThreadName from '../../lib/methods/getThreadName';
import getRoomInfo from '../../lib/methods/getRoomInfo'; import getRoomInfo from '../../lib/methods/getRoomInfo';
import { isIOS } from '../../utils/deviceInfo';
import { compareServerVersion, methods } from '../../lib/utils';
import styles from './styles'; import styles from './styles';
const QUERY_SIZE = 50;
class SearchMessagesView extends React.Component { class SearchMessagesView extends React.Component {
static navigationOptions = ({ navigation, route }) => { static navigationOptions = ({ navigation, route }) => {
const options = { const options = {
@ -43,6 +46,7 @@ class SearchMessagesView extends React.Component {
route: PropTypes.object, route: PropTypes.object,
user: PropTypes.object, user: PropTypes.object,
baseUrl: PropTypes.string, baseUrl: PropTypes.string,
serverVersion: PropTypes.string,
customEmojis: PropTypes.object, customEmojis: PropTypes.object,
theme: PropTypes.string, theme: PropTypes.string,
useRealName: PropTypes.bool useRealName: PropTypes.bool
@ -55,6 +59,7 @@ class SearchMessagesView extends React.Component {
messages: [], messages: [],
searchText: '' searchText: ''
}; };
this.offset = 0;
this.rid = props.route.params?.rid; this.rid = props.route.params?.rid;
this.t = props.route.params?.t; this.t = props.route.params?.t;
this.encrypted = props.route.params?.encrypted; this.encrypted = props.route.params?.encrypted;
@ -88,6 +93,9 @@ class SearchMessagesView extends React.Component {
// Handle encrypted rooms search messages // Handle encrypted rooms search messages
searchMessages = async searchText => { searchMessages = async searchText => {
if (!searchText) {
return [];
}
// If it's a encrypted, room we'll search only on the local stored messages // If it's a encrypted, room we'll search only on the local stored messages
if (this.encrypted) { if (this.encrypted) {
const db = database.active; const db = database.active;
@ -103,25 +111,33 @@ class SearchMessagesView extends React.Component {
.fetch(); .fetch();
} }
// If it's not a encrypted room, search messages on the server // If it's not a encrypted room, search messages on the server
const result = await RocketChat.searchMessages(this.rid, searchText); const result = await RocketChat.searchMessages(this.rid, searchText, QUERY_SIZE, this.offset);
if (result.success) { if (result.success) {
return result.messages; return result.messages;
} }
}; };
search = debounce(async searchText => { getMessages = async (searchText, debounced) => {
this.setState({ searchText, loading: true, messages: [] });
try { try {
const messages = await this.searchMessages(searchText); const messages = await this.searchMessages(searchText);
this.setState({ this.setState(prevState => ({
messages: messages || [], messages: debounced ? messages : [...prevState.messages, ...messages],
loading: false loading: false
}); }));
} catch (e) { } catch (e) {
this.setState({ loading: false }); this.setState({ loading: false });
log(e); log(e);
} }
};
search = searchText => {
this.offset = 0;
this.setState({ searchText, loading: true, messages: [] });
this.searchDebounced(searchText);
};
searchDebounced = debounce(async searchText => {
await this.getMessages(searchText, true);
}, 1000); }, 1000);
getCustomEmoji = name => { getCustomEmoji = name => {
@ -168,6 +184,23 @@ class SearchMessagesView extends React.Component {
} }
}; };
onEndReached = async () => {
const { serverVersion } = this.props;
const { searchText, messages, loading } = this.state;
if (
messages.length < this.offset ||
this.encrypted ||
loading ||
compareServerVersion(serverVersion, '3.17.0', methods.lowerThan)
) {
return;
}
this.setState({ loading: true });
this.offset += QUERY_SIZE;
await this.getMessages(searchText);
};
renderEmpty = () => { renderEmpty = () => {
const { theme } = this.props; const { theme } = this.props;
return ( return (
@ -212,8 +245,10 @@ class SearchMessagesView extends React.Component {
renderItem={this.renderItem} renderItem={this.renderItem}
style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]} style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]}
keyExtractor={item => item._id} keyExtractor={item => item._id}
onEndReached={this.load} onEndReached={this.onEndReached}
ListFooterComponent={loading ? <ActivityIndicator theme={theme} /> : null} ListFooterComponent={loading ? <ActivityIndicator theme={theme} /> : null}
onEndReachedThreshold={0.5}
removeClippedSubviews={isIOS}
{...scrollPersistTaps} {...scrollPersistTaps}
/> />
); );
@ -243,6 +278,7 @@ class SearchMessagesView extends React.Component {
} }
const mapStateToProps = state => ({ const mapStateToProps = state => ({
serverVersion: state.server.version,
baseUrl: state.server.server, baseUrl: state.server.server,
user: getUserSelector(state), user: getUserSelector(state),
useRealName: state.settings.UI_Use_Real_Name, useRealName: state.settings.UI_Use_Real_Name,