Skip to content

Commit

Permalink
Merge pull request #110 from ulu-telegram/search
Browse files Browse the repository at this point in the history
search in command menu
  • Loading branch information
ulugmer authored Nov 20, 2023
2 parents 71a41a8 + 461a286 commit 1b49cfa
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 4 deletions.
140 changes: 140 additions & 0 deletions src/components/common/AllUsersAndChats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React from 'react';
import { Command } from 'cmdk';
import { useCallback, useMemo } from '../../lib/teact/teact';
import { getActions, getGlobal } from '../../global';

import type { ApiChat, ApiUser } from '../../api/types';

import {
getChatTitle,
getChatTypeString,
getMainUsername, getUserFullName, isDeletedUser,
} from '../../global/helpers';
import { convertLayout } from '../../util/convertLayout';
import { unique } from '../../util/iteratees';
import renderText from './helpers/renderText';

import useLang from '../../hooks/useLang';

const AllUsersAndChats: React.FC<
{ close: () => void; searchQuery: string; topUserIds: string[] }> = ({ close, searchQuery, topUserIds }) => {
const global = getGlobal();
const usersById: Record<string, ApiUser> = global.users.byId;
const chatsById: Record<string, ApiChat> = global.chats.byId;
const { openChat, addRecentlyFoundChatId } = getActions();
const SEARCH_CLOSE_TIMEOUT_MS = 250;

const lang = useLang();

function getGroupStatus(chat: ApiChat) {
const chatTypeString = lang(getChatTypeString(chat));
const { membersCount } = chat;

if (chat.isRestricted) {
return chatTypeString === 'Channel' ? 'channel is inaccessible' : 'group is inaccessible';
}

if (!membersCount) {
return chatTypeString;
}

return chatTypeString === 'Channel'
? lang('Subscribers', membersCount, 'i')
: lang('Members', membersCount, 'i');
}

const renderName = (id: string, isUser: boolean): { content: React.ReactNode; value: string } => {
const NBSP = '\u00A0';
let content: React.ReactNode;
let value: string;

if (isUser) {
const user = usersById[id] as ApiUser;
if (isDeletedUser(user)) {
return { content: undefined, value: '' };
}
const name = getUserFullName(user) || NBSP;
const handle = getMainUsername(user) || NBSP;
const renderedName = renderText(name);
content = React.isValidElement(renderedName) ? renderedName : (
<span>
<span className="entity-name">{name}</span>
<span className="user-handle">{handle !== NBSP ? `@${handle}` : ''}</span>
</span>
);
value = `${name} ${handle !== NBSP ? handle : ''}`.trim();
} else {
const chat = chatsById[id] as ApiChat;
const title = getChatTitle(lang, chat) || 'Unknown Chat';
const groupStatus = getGroupStatus(chat);
content = (
<span>
<span className="chat-title">{title}</span>
<span className="chat-status">{groupStatus}</span>
</span>
);
value = title;
}

return { content, value };
};

const handleClick = useCallback((id: string) => {
openChat({ id, shouldReplaceHistory: true });
setTimeout(() => addRecentlyFoundChatId({ id }), SEARCH_CLOSE_TIMEOUT_MS);
close();
}, [openChat, addRecentlyFoundChatId, close]);

const handeSelect = useCallback((id: string) => () => handleClick(id), [handleClick]);

const ids = useMemo(() => {
const convertedSearchQuery = convertLayout(searchQuery).toLowerCase();
const userAndChatIds = unique([...Object.keys(usersById), ...Object.keys(chatsById)]);
return userAndChatIds.filter((id) => {
if (topUserIds && topUserIds.slice(0, 3).includes(id)) {
return false;
}
const isUser = usersById.hasOwnProperty(id);
if (isUser) {
const user = usersById[id];
if (isDeletedUser(user)) return false;
const name = getUserFullName(user) || ''; // Запасной вариант для 'undefined'
return name.toLowerCase().includes(searchQuery.toLowerCase())
|| name.toLowerCase().includes(convertedSearchQuery);
} else {
const chat = chatsById[id];
const title = getChatTitle(lang, chat) || ''; // Запасной вариант для 'undefined'
return title.toLowerCase().includes(searchQuery.toLowerCase())
|| title.toLowerCase().includes(convertedSearchQuery);
}
});
}, [usersById, chatsById, searchQuery, lang, topUserIds]);

if (!searchQuery) {
// eslint-disable-next-line no-null/no-null
return null;
}

return (
<Command.Group heading={`Search for "${searchQuery}"`}>
{ids.map((id) => {
const isUser = usersById.hasOwnProperty(id);
const { content, value } = renderName(id, isUser);
// eslint-disable-next-line no-null/no-null
if (!content) return null;

return (
<Command.Item
key={id}
value={value}
onSelect={handeSelect(id)}
>
{content}
</Command.Item>
);
})}
</Command.Group>
);
};

export default AllUsersAndChats;
57 changes: 56 additions & 1 deletion src/components/main/CommandMenu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,61 @@
display: flex;
align-items: center;
justify-content: center;
height: 48px;
height: 0px;
white-space: pre-wrap;
color: var(--gray11);
}

.global-search {
display: none;
}

/* Показать элемент глобального поиска, когда нет результатов */
[cmdk-empty] + .global-search {
content-visibility: auto;

cursor: pointer;
background: none;
width: 100%;
border: none;
margin: 0;
height: 48px;
border-radius: 8px;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
padding: 0 16px !important;
color: var(--gray11);
user-select: none;
will-change: background, color;
transition: background 150ms ease, color 150ms ease;
transition-property: none;

&[data-selected='true'] {
background: #ffffff14;
}

&[data-disabled='true'] {
opacity: 0.4;
cursor: not-allowed;
}

&:active {
transition-property: background;
background: var(--color-chat-active);
}

& + [cmdk-item] {
margin-top: 4px;
}
}

.global-search:hover, .global-search[data-selected='true'] {
/* Ваши стили для hover, например изменение фона */
background: #ffffff14; /* Пример фона при наведении */
}

.cmdk-backdrop {
position: fixed;
left: -100vw;
Expand Down Expand Up @@ -215,3 +265,8 @@
margin-left: 0.625rem;
color: #aaaaaa
}

.chat-status {
margin-left: 0.625rem;
color: #aaaaaa
}
26 changes: 23 additions & 3 deletions src/components/main/CommandMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable react/self-closing-comp */
/* eslint-disable arrow-parens */
/* eslint-disable react/no-array-index-key */
/* eslint-disable @typescript-eslint/no-shadow */
Expand Down Expand Up @@ -26,6 +27,8 @@ import useArchiver from '../../hooks/useArchiver';
import useCommands from '../../hooks/useCommands';
import { useJune } from '../../hooks/useJune';

import AllUsersAndChats from '../common/AllUsersAndChats';

import './CommandMenu.scss';

const cmdkElement = document.getElementById('cmdk-root');
Expand Down Expand Up @@ -92,7 +95,7 @@ const SuggestedContacts: FC<SuggestedContactsProps> = ({ topUserIds, usersById,

return (
<Command.Group heading="Suggested contacts">
{topUserIds.map((userId) => {
{topUserIds.slice(0, 3).map((userId) => { // take the first 3 elements
const { displayedName, valueString } = renderName(userId);
return (
<Command.Item key={userId} value={valueString} onSelect={() => handleClick(userId)}>
Expand Down Expand Up @@ -302,6 +305,7 @@ const CommandMenu: FC<CommandMenuProps> = ({ topUserIds, usersById }) => {
const close = useCallback(() => {
setOpen(false);
setPages(['home']);
setInputValue('');
}, []);

// Toggle the menu when ⌘K is pressed
Expand Down Expand Up @@ -473,13 +477,12 @@ const CommandMenu: FC<CommandMenuProps> = ({ topUserIds, usersById }) => {
onValueChange={handleInputChange}
value={inputValue}
onKeyDown={(e) => {
if (e.key === 'Backspace') {
if (e.key === 'Backspace' && inputValue === '') {
handleBack();
}
}}
/>
<Command.List>
<Command.Empty>No results found.</Command.Empty>
{activePage === 'home' && (
<HomePage
/* setPages={setPages} */
Expand Down Expand Up @@ -512,7 +515,24 @@ const CommandMenu: FC<CommandMenuProps> = ({ topUserIds, usersById }) => {
handleCreateFolder={handleCreateFolder}
/>
)}
<AllUsersAndChats
close={close}
searchQuery={inputValue}
topUserIds={topUserIds}
/>
</Command.List>
<Command.Empty></Command.Empty>
<button className="global-search" onClick={handleSearchFocus}>
<i className="icon icon-search" />
<span>
<span>No results found</span>
<span className="user-handle">Go to advanced search</span>
</span>
<span className="shortcuts">
<span className="kbd"></span>
<span className="kbd">/</span>
</span>
</button>
</Command.Dialog>
);

Expand Down

0 comments on commit 1b49cfa

Please sign in to comment.