Rocket.Chat.ReactNative/app/containers/MessageComposer/hooks/useAutocomplete.ts

180 lines
5.0 KiB
TypeScript
Raw Normal View History

2024-01-25 14:11:07 +00:00
import { useEffect, useState } from 'react';
import { Q } from '@nozbe/watermelondb';
import { IAutocompleteEmoji, IAutocompleteUserRoom, TAutocompleteItem, TAutocompleteType } from '../interfaces';
import { search } from '../../../lib/methods';
import { sanitizeLikeString } from '../../../lib/database/utils';
import database from '../../../lib/database';
import { emojis } from '../../../lib/constants';
import { ICustomEmoji } from '../../../definitions';
import { Services } from '../../../lib/services';
import log from '../../../lib/methods/helpers/log';
import I18n from '../../../i18n';
import { NO_CANNED_RESPONSES } from '../constants';
const MENTIONS_COUNT_TO_DISPLAY = 4;
const getCustomEmojis = async (keyword: string): Promise<ICustomEmoji[]> => {
const likeString = sanitizeLikeString(keyword);
const whereClause = [];
if (likeString) {
whereClause.push(Q.where('name', Q.like(`${likeString}%`)));
}
const db = database.active;
const customEmojisCollection = db.get('custom_emojis');
const customEmojis = await (await customEmojisCollection.query(...whereClause).fetch())
.slice(0, MENTIONS_COUNT_TO_DISPLAY)
.map(emoji => ({
name: emoji.name,
extension: emoji.extension
}));
return customEmojis;
};
export const useAutocomplete = ({
text,
type,
rid,
commandParams
}: {
rid?: string;
type: TAutocompleteType;
text: string;
commandParams?: string;
}): TAutocompleteItem[] => {
const [items, setItems] = useState<TAutocompleteItem[]>([]);
useEffect(() => {
const getAutocomplete = async () => {
try {
if (!rid || !type) {
setItems([]);
return;
}
// remove existing loading skeleton from items
const loadingIndex = items.findIndex(item => item.id === 'loading');
if (loadingIndex !== -1) {
items.splice(loadingIndex, 1);
}
// add loading skeleton
items.unshift({ id: 'loading', type: 'loading' });
setItems(items);
if (type === '@' || type === '#') {
const res = await search({ text, filterRooms: type === '#', filterUsers: type === '@', rid });
const parsedRes: IAutocompleteUserRoom[] = res
// TODO: need to refactor search to have a more predictable return type
.map((item: any) => ({
id: type === '@' ? item._id : item.rid,
title: item.fname || item.name || item.username,
subtitle: item.username || item.name,
outside: item.outside,
t: item.t ?? 'd',
status: item.status,
teamMain: item.teamMain,
type
})) as IAutocompleteUserRoom[];
if (type === '@') {
if ('all'.includes(text.toLocaleLowerCase())) {
parsedRes.push({
id: 'all',
title: 'all',
subtitle: I18n.t('Notify_all_in_this_room'),
type,
t: 'd'
});
}
if ('here'.includes(text.toLocaleLowerCase())) {
parsedRes.push({
id: 'here',
title: 'here',
subtitle: I18n.t('Notify_active_in_this_room'),
type,
t: 'd'
});
}
}
setItems(parsedRes);
}
if (type === ':') {
const customEmojis = await getCustomEmojis(text);
const filteredStandardEmojis = emojis.filter(emoji => emoji.indexOf(text) !== -1).slice(0, MENTIONS_COUNT_TO_DISPLAY);
let mergedEmojis: IAutocompleteEmoji[] = customEmojis.map(emoji => ({
id: emoji.name,
emoji,
type
}));
mergedEmojis = mergedEmojis.concat(
filteredStandardEmojis.map(emoji => ({
id: emoji,
emoji,
type
}))
);
setItems(mergedEmojis);
}
if (type === '/') {
const db = database.active;
const commandsCollection = db.get('slash_commands');
const likeString = sanitizeLikeString(text);
const commands = await (
await commandsCollection.query(Q.where('id', Q.like(`${likeString}%`))).fetch()
).map(command => ({
id: command.id,
title: command.id,
subtitle: command.description,
type
}));
setItems(commands);
}
if (type === '/preview') {
if (!commandParams) {
setItems([]);
return;
}
const response = await Services.getCommandPreview(text, rid, commandParams);
if (response.success) {
const previewItems = (response.preview?.items || []).map(item => ({
id: item.id,
preview: item,
type,
text,
params: commandParams
}));
setItems(previewItems);
}
}
if (type === '!') {
const res = await Services.getListCannedResponse({ text });
if (res.success) {
if (res.cannedResponses.length === 0) {
setItems([
{
id: NO_CANNED_RESPONSES,
title: NO_CANNED_RESPONSES,
type
}
]);
return;
}
const cannedResponses = res.cannedResponses.map(cannedResponse => ({
id: cannedResponse._id,
title: cannedResponse.shortcut,
subtitle: cannedResponse.text,
type
}));
setItems(cannedResponses);
}
}
} catch (e) {
log(e);
setItems([]);
}
};
getAutocomplete();
}, [text, type, rid, commandParams]);
return items;
};