[IMPROVE] Spotlight users order (#4527)
* [IMPROVE] Spotlight users order * minor tweak query * minor tweak removing query and using regex * minor tweak * minor tweak, make ts happy * fix the ts * fix lint and type TSearch
This commit is contained in:
parent
50e41e2428
commit
5e8698086c
|
@ -658,7 +658,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
getUsers = debounce(async (keyword: any) => {
|
getUsers = debounce(async (keyword: any) => {
|
||||||
let res = await search({ text: keyword, filterRooms: false, filterUsers: true });
|
const { rid } = this.props;
|
||||||
|
let res = await search({ text: keyword, filterRooms: false, filterUsers: true, rid });
|
||||||
res = [...this.getFixedMentions(keyword), ...res];
|
res = [...this.getFixedMentions(keyword), ...res];
|
||||||
this.setState({ mentions: res, mentionLoading: false });
|
this.setState({ mentions: res, mentionLoading: false });
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
|
@ -226,5 +226,8 @@ export const defaultSettings = {
|
||||||
},
|
},
|
||||||
Accounts_AllowDeleteOwnAccount: {
|
Accounts_AllowDeleteOwnAccount: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean'
|
||||||
|
},
|
||||||
|
Number_of_users_autocomplete_suggestions: {
|
||||||
|
type: 'valueAsNumber'
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -2,13 +2,16 @@ import { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
import { sanitizeLikeString } from '../database/utils';
|
import { sanitizeLikeString } from '../database/utils';
|
||||||
import database from '../database/index';
|
import database from '../database/index';
|
||||||
|
import { store as reduxStore } from '../store/auxStore';
|
||||||
import { spotlight } from '../services/restApi';
|
import { spotlight } from '../services/restApi';
|
||||||
import { ISearch, ISearchLocal, SubscriptionType } from '../../definitions';
|
import { ISearch, ISearchLocal, IUserMessage, SubscriptionType } from '../../definitions';
|
||||||
import { isGroupChat } from './helpers';
|
import { isGroupChat } from './helpers';
|
||||||
|
|
||||||
|
export type TSearch = ISearchLocal | IUserMessage | ISearch;
|
||||||
|
|
||||||
let debounce: null | ((reason: string) => void) = null;
|
let debounce: null | ((reason: string) => void) = null;
|
||||||
|
|
||||||
export const localSearch = async ({ text = '', filterUsers = true, filterRooms = true }): Promise<ISearchLocal[]> => {
|
export const localSearchSubscription = async ({ text = '', filterUsers = true, filterRooms = true }): Promise<ISearchLocal[]> => {
|
||||||
const searchText = text.trim();
|
const searchText = text.trim();
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const likeString = sanitizeLikeString(searchText);
|
const likeString = sanitizeLikeString(searchText);
|
||||||
|
@ -41,29 +44,60 @@ export const localSearch = async ({ text = '', filterUsers = true, filterRooms =
|
||||||
return search;
|
return search;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const search = async ({ text = '', filterUsers = true, filterRooms = true }): Promise<(ISearch | ISearchLocal)[]> => {
|
export const localSearchUsersMessageByRid = async ({ text = '', rid = '' }): Promise<IUserMessage[]> => {
|
||||||
|
const userId = reduxStore.getState().login.user.id;
|
||||||
|
const numberOfSuggestions = reduxStore.getState().settings.Number_of_users_autocomplete_suggestions as number;
|
||||||
|
const searchText = text.trim();
|
||||||
|
const db = database.active;
|
||||||
|
const likeString = sanitizeLikeString(searchText);
|
||||||
|
const messages = await db
|
||||||
|
.get('messages')
|
||||||
|
.query(
|
||||||
|
Q.and(Q.where('rid', rid), Q.where('u', Q.notLike(`%${userId}%`)), Q.where('t', null)),
|
||||||
|
Q.experimentalSortBy('ts', Q.desc),
|
||||||
|
Q.experimentalTake(50)
|
||||||
|
)
|
||||||
|
.fetch();
|
||||||
|
|
||||||
|
const regExp = new RegExp(`${likeString}`, 'i');
|
||||||
|
const users = messages.map(message => message.u);
|
||||||
|
|
||||||
|
const usersFromLocal = users
|
||||||
|
.filter((item1, index) => users.findIndex(item2 => item2._id === item1._id) === index) // Remove duplicated data from response
|
||||||
|
.filter(user => user?.name?.match(regExp) || user?.username?.match(regExp))
|
||||||
|
.slice(0, text ? 2 : numberOfSuggestions);
|
||||||
|
|
||||||
|
return usersFromLocal;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const search = async ({ text = '', filterUsers = true, filterRooms = true, rid = '' }): Promise<TSearch[]> => {
|
||||||
const searchText = text.trim();
|
const searchText = text.trim();
|
||||||
|
|
||||||
if (debounce) {
|
if (debounce) {
|
||||||
debounce('cancel');
|
debounce('cancel');
|
||||||
}
|
}
|
||||||
|
|
||||||
const localSearchData = await localSearch({ text, filterUsers, filterRooms });
|
let localSearchData = [];
|
||||||
const usernames = localSearchData.map(sub => sub.name);
|
if (rid) {
|
||||||
|
localSearchData = await localSearchUsersMessageByRid({ text, rid });
|
||||||
|
} else {
|
||||||
|
localSearchData = await localSearchSubscription({ text, filterUsers, filterRooms });
|
||||||
|
}
|
||||||
|
const usernames = localSearchData.map(sub => sub.name as string);
|
||||||
|
|
||||||
const data = localSearchData as (ISearch | ISearchLocal)[];
|
const data: TSearch[] = localSearchData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (localSearchData.length < 7) {
|
if (searchText && localSearchData.length < 7) {
|
||||||
const { users, rooms } = (await Promise.race([
|
const { users, rooms } = (await Promise.race([
|
||||||
spotlight(searchText, usernames, { users: filterUsers, rooms: filterRooms }),
|
spotlight(searchText, usernames, { users: filterUsers, rooms: filterRooms }, rid),
|
||||||
new Promise((resolve, reject) => (debounce = reject))
|
new Promise((resolve, reject) => (debounce = reject))
|
||||||
])) as { users: ISearch[]; rooms: ISearch[] };
|
])) as { users: ISearch[]; rooms: ISearch[] };
|
||||||
|
|
||||||
if (filterUsers) {
|
if (filterUsers) {
|
||||||
users
|
users
|
||||||
.filter((item1, index) => users.findIndex(item2 => item2._id === item1._id) === index) // Remove duplicated data from response
|
.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
|
.filter(user => !data.some(sub => 'username' in sub && user.username === sub.username)) // Make sure to remove users already on local database
|
||||||
.forEach(user => {
|
.forEach(user => {
|
||||||
data.push({
|
data.push({
|
||||||
...user,
|
...user,
|
||||||
|
@ -77,7 +111,7 @@ export const search = async ({ text = '', filterUsers = true, filterRooms = true
|
||||||
if (filterRooms) {
|
if (filterRooms) {
|
||||||
rooms.forEach(room => {
|
rooms.forEach(room => {
|
||||||
// Check if it exists on local database
|
// Check if it exists on local database
|
||||||
const index = data.findIndex(item => item.rid === room._id);
|
const index = data.findIndex(item => 'rid' in item && item.rid === room._id);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
data.push({
|
data.push({
|
||||||
...room,
|
...room,
|
||||||
|
|
|
@ -89,9 +89,16 @@ export const forgotPassword = (email: string) =>
|
||||||
export const sendConfirmationEmail = (email: string): Promise<{ message: string; success: boolean }> =>
|
export const sendConfirmationEmail = (email: string): Promise<{ message: string; success: boolean }> =>
|
||||||
sdk.methodCallWrapper('sendConfirmationEmail', email);
|
sdk.methodCallWrapper('sendConfirmationEmail', email);
|
||||||
|
|
||||||
export const spotlight = (search: string, usernames: string[], type: { users: boolean; rooms: boolean }): Promise<ISpotlight> =>
|
export const spotlight = (
|
||||||
|
search: string,
|
||||||
|
usernames: string[],
|
||||||
|
type: { users: boolean; rooms: boolean },
|
||||||
|
rid?: string
|
||||||
|
): Promise<ISpotlight> =>
|
||||||
// RC 0.51.0
|
// RC 0.51.0
|
||||||
sdk.methodCallWrapper('spotlight', search, usernames, type);
|
rid
|
||||||
|
? sdk.methodCallWrapper('spotlight', search, usernames, type, rid)
|
||||||
|
: sdk.methodCallWrapper('spotlight', search, usernames, type);
|
||||||
|
|
||||||
export const createDirectMessage = (username: string) =>
|
export const createDirectMessage = (username: string) =>
|
||||||
// RC 0.59.0
|
// RC 0.59.0
|
||||||
|
|
|
@ -7,7 +7,7 @@ import I18n from '../../i18n';
|
||||||
import { getAvatarURL } from '../../lib/methods/helpers/getAvatarUrl';
|
import { getAvatarURL } from '../../lib/methods/helpers/getAvatarUrl';
|
||||||
import { ICreateDiscussionViewSelectChannel } from './interfaces';
|
import { ICreateDiscussionViewSelectChannel } from './interfaces';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { localSearch } from '../../lib/methods';
|
import { localSearchSubscription } from '../../lib/methods';
|
||||||
import { getRoomAvatar, getRoomTitle } from '../../lib/methods/helpers';
|
import { getRoomAvatar, getRoomTitle } from '../../lib/methods/helpers';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ const SelectChannel = ({
|
||||||
|
|
||||||
const getChannels = async (keyword = '') => {
|
const getChannels = async (keyword = '') => {
|
||||||
try {
|
try {
|
||||||
const res = (await localSearch({ text: keyword, filterUsers: false })) as ISearchLocal[];
|
const res = (await localSearchSubscription({ text: keyword, filterUsers: false })) as ISearchLocal[];
|
||||||
setChannels(res);
|
setChannels(res);
|
||||||
return res.map(channel => ({
|
return res.map(channel => ({
|
||||||
value: channel,
|
value: channel,
|
||||||
|
|
|
@ -13,7 +13,6 @@ import * as List from '../../containers/List';
|
||||||
import { sendLoadingEvent } from '../../containers/Loading';
|
import { sendLoadingEvent } from '../../containers/Loading';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { ISearch, ISearchLocal } from '../../definitions';
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import UserItem from '../../containers/UserItem';
|
import UserItem from '../../containers/UserItem';
|
||||||
|
@ -23,7 +22,7 @@ import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { showErrorAlert } from '../../lib/methods/helpers/info';
|
import { showErrorAlert } from '../../lib/methods/helpers/info';
|
||||||
import log, { events, logEvent } from '../../lib/methods/helpers/log';
|
import log, { events, logEvent } from '../../lib/methods/helpers/log';
|
||||||
import { search as searchMethod } from '../../lib/methods';
|
import { search as searchMethod, TSearch } from '../../lib/methods';
|
||||||
import { isGroupChat as isGroupChatMethod } from '../../lib/methods/helpers';
|
import { isGroupChat as isGroupChatMethod } from '../../lib/methods/helpers';
|
||||||
import { useAppSelector } from '../../lib/hooks';
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
|
@ -31,11 +30,9 @@ import Header from './Header';
|
||||||
type TRoute = RouteProp<ChatsStackParamList, 'SelectedUsersView'>;
|
type TRoute = RouteProp<ChatsStackParamList, 'SelectedUsersView'>;
|
||||||
type TNavigation = StackNavigationProp<ChatsStackParamList, 'SelectedUsersView'>;
|
type TNavigation = StackNavigationProp<ChatsStackParamList, 'SelectedUsersView'>;
|
||||||
|
|
||||||
type TSearchItem = ISearch | ISearchLocal;
|
|
||||||
|
|
||||||
const SelectedUsersView = () => {
|
const SelectedUsersView = () => {
|
||||||
const [chats, setChats] = useState<ISelectedUser[]>([]);
|
const [chats, setChats] = useState<ISelectedUser[]>([]);
|
||||||
const [search, setSearch] = useState<TSearchItem[]>([]);
|
const [search, setSearch] = useState<TSearch[]>([]);
|
||||||
|
|
||||||
const { maxUsers, showButton, title, buttonText, nextAction } = useRoute<TRoute>().params;
|
const { maxUsers, showButton, title, buttonText, nextAction } = useRoute<TRoute>().params;
|
||||||
const navigation = useNavigation<TNavigation>();
|
const navigation = useNavigation<TNavigation>();
|
||||||
|
|
Loading…
Reference in New Issue