Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions app/definitions/ISearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ILastMessage } from './IMessage';

export interface ISearchLocal {
avatarETag: string;
rid: string;
name: string;
t: string;
fname: string;
encrypted: boolean | null;
lastMessage?: ILastMessage;
}

export interface ISearch extends ISearchLocal {
// return only from api search
_id: string;
status?: string;
username: string;
outside?: boolean;
search?: boolean;
}
4 changes: 2 additions & 2 deletions app/definitions/ISubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export enum SubscriptionType {
DIRECT = 'd',
CHANNEL = 'c',
OMNICHANNEL = 'l',
E2E = 'e2e',
THREAD = 'thread' // FIXME: this is not a type of subscription
THREAD = 'thread', // FIXME: this is not a type of subscription
E2E = 'e2e'
}

export interface IVisitor {
Expand Down
1 change: 1 addition & 0 deletions app/definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export * from './IServerHistory';
export * from './IRocketChat';
export * from './ICertificate';
export * from './IUrl';
export * from './ISearch';

export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> {
navigation: StackNavigationProp<T, S>;
Expand Down
98 changes: 98 additions & 0 deletions app/lib/rocketchat/methods/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Q } from '@nozbe/watermelondb';

import { sanitizeLikeString } from '../../database/utils';
import database from '../../database/index';
import { spotlight } from '../services/restApi';
import isGroupChat from './isGroupChat';
import { ISearch, ISearchLocal, SubscriptionType } from '../../../definitions';

let debounce: null | ((reason: string) => void) = null;

export const localSearch = async ({ text = '', filterUsers = true, filterRooms = true }): Promise<ISearchLocal[]> => {
const searchText = text.trim();
const db = database.active;
const likeString = sanitizeLikeString(searchText);
let subscriptions = await db
.get('subscriptions')
.query(
Q.or(Q.where('name', Q.like(`%${likeString}%`)), Q.where('fname', Q.like(`%${likeString}%`))),
Q.experimentalSortBy('room_updated_at', Q.desc)
)
.fetch();

if (filterUsers && !filterRooms) {
subscriptions = subscriptions.filter(item => item.t === 'd' && !isGroupChat(item));
} else if (!filterUsers && filterRooms) {
subscriptions = subscriptions.filter(item => item.t !== 'd' || isGroupChat(item));
}

const sliceSubscriptions = subscriptions.slice(0, 7);

const search = sliceSubscriptions.map(sub => ({
rid: sub.rid,
name: sub.name,
fname: sub?.fname || '',
avatarETag: sub?.avatarETag || '',
t: sub.t,
encrypted: sub?.encrypted || null,
lastMessage: sub.lastMessage,
...(sub.teamId && { teamId: sub.teamId })
}));

return search;
};

export const search = async ({ text = '', filterUsers = true, filterRooms = true }): Promise<(ISearch | ISearchLocal)[]> => {
Comment thread
dnlsilva marked this conversation as resolved.
const searchText = text.trim();

if (debounce) {
debounce('cancel');
}

const localSearchData = await localSearch({ text, filterUsers, filterRooms });
const usernames = localSearchData.map(sub => sub.name);

const data: (ISearch | ISearchLocal)[] = localSearchData;

try {
if (localSearchData.length < 7) {
const { users, rooms } = (await Promise.race([
spotlight(searchText, usernames, { users: filterUsers, rooms: filterRooms }),
new Promise((resolve, reject) => (debounce = reject))
])) as { users: ISearch[]; rooms: ISearch[] };

if (filterUsers) {
users
.filter((item1, index) => users.findIndex(item2 => item2._id === item1._id) === index) // Remove duplicated data from response
.filter(user => !data.some(sub => user.username === sub.name)) // Make sure to remove users already on local database
.forEach(user => {
data.push({
...user,
rid: user.username,
name: user.username,
t: SubscriptionType.DIRECT,
search: true
});
});
}
if (filterRooms) {
rooms.forEach(room => {
// Check if it exists on local database
const index = data.findIndex(item => item.rid === room._id);
if (index === -1) {
data.push({
...room,
rid: room._id,
search: true
});
}
});
}
}
debounce = null;
return data;
} catch (e) {
console.warn(e);
return data;
}
};
89 changes: 2 additions & 87 deletions app/lib/rocketchat/rocketchat.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import getRoom from './methods/getRoom';
import isGroupChat from './methods/isGroupChat';
import roomTypeToApiType from './methods/roomTypeToApiType';
import getUserInfo from './services/getUserInfo';
import * as search from './methods/search';
// Services
import sdk from './services/sdk';
import toggleFavorite from './services/toggleFavorite';
Expand All @@ -83,6 +84,7 @@ const RocketChat = {
CURRENT_SERVER,
CERTIFICATE_KEY,
...restAPis,
...search,
callJitsi,
callJitsiWithoutServer,
async subscribeRooms() {
Expand Down Expand Up @@ -628,93 +630,6 @@ const RocketChat = {
getRooms,
readMessages,
resendMessage,

async localSearch({ text, filterUsers = true, filterRooms = true }) {
const searchText = text.trim();
const db = database.active;
const likeString = sanitizeLikeString(searchText);
let data = await db
.get('subscriptions')
.query(
Q.or(Q.where('name', Q.like(`%${likeString}%`)), Q.where('fname', Q.like(`%${likeString}%`))),
Q.experimentalSortBy('room_updated_at', Q.desc)
)
.fetch();

if (filterUsers && !filterRooms) {
data = data.filter(item => item.t === 'd' && !RocketChat.isGroupChat(item));
} else if (!filterUsers && filterRooms) {
data = data.filter(item => item.t !== 'd' || RocketChat.isGroupChat(item));
}

data = data.slice(0, 7);

data = data.map(sub => ({
rid: sub.rid,
name: sub.name,
fname: sub.fname,
avatarETag: sub.avatarETag,
t: sub.t,
encrypted: sub.encrypted,
lastMessage: sub.lastMessage,
...(sub.teamId && { teamId: sub.teamId })
}));

return data;
},

async search({ text, filterUsers = true, filterRooms = true }) {
const searchText = text.trim();

if (this.oldPromise) {
this.oldPromise('cancel');
}

const data = await this.localSearch({ text, filterUsers, filterRooms });

const usernames = data.map(sub => sub.name);
try {
if (data.length < 7) {
const { users, rooms } = await Promise.race([
RocketChat.spotlight(searchText, usernames, { users: filterUsers, rooms: filterRooms }),
new Promise((resolve, reject) => (this.oldPromise = reject))
]);
if (filterUsers) {
users
.filter((item1, index) => users.findIndex(item2 => item2._id === item1._id) === index) // Remove duplicated data from response
.filter(user => !data.some(sub => user.username === sub.name)) // Make sure to remove users already on local database
.forEach(user => {
data.push({
...user,
rid: user.username,
name: user.username,
t: 'd',
search: true
});
});
}
if (filterRooms) {
rooms.forEach(room => {
// Check if it exists on local database
const index = data.findIndex(item => item.rid === room._id);
if (index === -1) {
data.push({
rid: room._id,
...room,
search: true
});
}
});
}
}
delete this.oldPromise;
return data;
} catch (e) {
console.warn(e);
return data;
// return [];
}
},
createGroupChat() {
const { users } = reduxStore.getState().selectedUsers;
const usernames = users.map(u => u.name).join(',');
Expand Down
2 changes: 1 addition & 1 deletion app/lib/rocketchat/services/restApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const forgotPassword = (email: string): any =>

export const sendConfirmationEmail = (email: string) => sdk.methodCallWrapper('sendConfirmationEmail', email);

export const spotlight = (search: string, usernames: string, type: { users: boolean; rooms: boolean }) =>
export const spotlight = (search: string, usernames: string[], type: { users: boolean; rooms: boolean }) =>
// RC 0.51.0
sdk.methodCallWrapper('spotlight', search, usernames, type);

Expand Down
16 changes: 8 additions & 8 deletions app/views/CreateDiscussionView/SelectChannel.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { useState } from 'react';
import { Text } from 'react-native';

import debounce from '../../utils/debounce';
import { avatarURL } from '../../utils/avatar';
import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n';
import { MultiSelect } from '../../containers/UIKit/MultiSelect';
import { themes } from '../../constants/colors';
import { TSubscriptionModel } from '../../definitions/ISubscription';
import styles from './styles';
import { MultiSelect } from '../../containers/UIKit/MultiSelect';
import { ISearchLocal } from '../../definitions';
import I18n from '../../i18n';
import RocketChat from '../../lib/rocketchat';
import { avatarURL } from '../../utils/avatar';
import debounce from '../../utils/debounce';
import { ICreateDiscussionViewSelectChannel } from './interfaces';
import styles from './styles';

const SelectChannel = ({
server,
Expand All @@ -21,7 +21,7 @@ const SelectChannel = ({
serverVersion,
theme
}: ICreateDiscussionViewSelectChannel): JSX.Element => {
const [channels, setChannels] = useState<TSubscriptionModel[]>([]);
const [channels, setChannels] = useState<ISearchLocal[]>([]);

const getChannels = debounce(async (keyword = '') => {
try {
Expand Down
18 changes: 3 additions & 15 deletions app/views/NewMessageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import * as List from '../containers/List';
import SafeAreaView from '../containers/SafeAreaView';
import SearchBox from '../containers/SearchBox';
import StatusBar from '../containers/StatusBar';
import { IApplicationState, IBaseScreen, TSubscriptionModel } from '../definitions';
import { IApplicationState, IBaseScreen, ISearch, TSubscriptionModel } from '../definitions';
import I18n from '../i18n';
import database from '../lib/database';
import { CustomIcon } from '../lib/Icons';
Expand Down Expand Up @@ -55,18 +55,6 @@ interface IButton {
first?: boolean;
}

interface ISearch {
_id: string;
status: string;
username: string;
avatarETag: string;
outside: boolean;
rid: string;
name: string;
t: string;
search: boolean;
}

interface INewMessageViewState {
search: (ISearch | TSubscriptionModel)[];
chats: TSubscriptionModel[];
Expand Down Expand Up @@ -149,7 +137,7 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
};

search = async (text: string) => {
const result: ISearch[] | TSubscriptionModel[] = await RocketChat.search({ text, filterRooms: false });
const result = (await RocketChat.search({ text, filterRooms: false })) as ISearch[];
this.setState({
search: result
});
Expand Down Expand Up @@ -313,7 +301,7 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
<FlatList
data={search.length > 0 ? search : chats}
extraData={this.state}
keyExtractor={item => item._id}
keyExtractor={item => item._id || item.rid}
Comment thread
diegolmello marked this conversation as resolved.
ListHeaderComponent={this.renderHeader}
renderItem={this.renderItem}
ItemSeparatorComponent={List.Separator}
Expand Down