import type { FC } from '../../lib/teact/teact'; import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, useState, } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import type { ApiChat, ApiReaction, ApiReactionKey, ApiSavedReactionTag, } from '../../api/types'; import type { ThreadId } from '../../types'; import { requestMutation } from '../../lib/fasterdom/fasterdom'; import { getIsSavedDialog, getReactionKey, isSameReaction } from '../../global/helpers'; import { selectChat, selectCurrentMessageList, selectCurrentTextSearch, selectIsChatWithSelf, selectIsCurrentUserPremium, selectTabState, } from '../../global/selectors'; import { getDayStartAt } from '../../util/dates/dateFormat'; import { debounce } from '../../util/schedulers'; import { IS_IOS } from '../../util/windowEnvironment'; import useHorizontalScroll from '../../hooks/useHorizontalScroll'; import useLastCallback from '../../hooks/useLastCallback'; import Button from '../ui/Button'; import SearchInput from '../ui/SearchInput'; import SavedTagButton from './message/reactions/SavedTagButton'; import './MobileSearch.scss'; export type OwnProps = { isActive: boolean; }; type StateProps = { isActive?: boolean; chat?: ApiChat; threadId?: ThreadId; query?: string; savedTags?: Record; searchTag?: ApiReaction; totalCount?: number; foundIds?: number[]; isHistoryCalendarOpen?: boolean; isCurrentUserPremium?: boolean; }; const runDebouncedForSearch = debounce((cb) => cb(), 200, false); const MobileSearchFooter: FC = ({ isActive, chat, threadId, query, savedTags, searchTag, totalCount, foundIds, isHistoryCalendarOpen, isCurrentUserPremium, }) => { const { setLocalTextSearchQuery, setLocalTextSearchTag, searchTextMessagesLocal, focusMessage, closeLocalTextSearch, openHistoryCalendar, openPremiumModal, loadSavedReactionTags, } = getActions(); // eslint-disable-next-line no-null/no-null const inputRef = useRef(null); // eslint-disable-next-line no-null/no-null const tagsRef = useRef(null); const [focusedIndex, setFocusedIndex] = useState(0); const hasQueryData = Boolean(query || searchTag); // Fix for iOS keyboard useEffect(() => { const { visualViewport } = window as any; if (!visualViewport) { return undefined; } const mainEl = document.getElementById('Main') as HTMLDivElement; const handleResize = () => { const { activeElement } = document; if (activeElement && (activeElement === inputRef.current)) { const { pageTop, height } = visualViewport; requestMutation(() => { mainEl.style.transform = `translateY(${pageTop}px)`; mainEl.style.height = `${height}px`; document.documentElement.scrollTop = pageTop; }); } else { requestMutation(() => { mainEl.style.transform = ''; mainEl.style.height = ''; }); } }; visualViewport.addEventListener('resize', handleResize); return () => { visualViewport.removeEventListener('resize', handleResize); }; }, []); // Focus message useEffect(() => { if (chat?.id && foundIds?.length) { focusMessage({ chatId: chat.id, messageId: foundIds[0], threadId }); setFocusedIndex(0); } else { setFocusedIndex(-1); } }, [chat?.id, focusMessage, foundIds, threadId]); // Disable native up/down buttons on iOS useLayoutEffect(() => { if (!IS_IOS) return; Array.from(document.querySelectorAll('input')).forEach((input) => { input.disabled = Boolean(isActive && input !== inputRef.current); }); }, [isActive]); // Blur on exit useEffect(() => { if (!isActive) { inputRef.current!.blur(); } }, [isActive]); useEffect(() => { const searchInput = document.querySelector('#MobileSearch input')!; searchInput.blur(); }, [isHistoryCalendarOpen]); const tags = useMemo(() => { if (!savedTags) return undefined; return Object.values(savedTags); }, [savedTags]); const hasTags = Boolean(tags?.length); const areTagsDisabled = hasTags && !isCurrentUserPremium; useHorizontalScroll(tagsRef, !hasTags); useEffect(() => { if (isActive) loadSavedReactionTags(); }, [hasTags, isActive]); const handleMessageSearchQueryChange = useLastCallback((newQuery: string) => { setLocalTextSearchQuery({ query: newQuery }); if (hasQueryData) { runDebouncedForSearch(searchTextMessagesLocal); } }); const handleTagClick = useLastCallback((tag: ApiReaction) => { if (areTagsDisabled) { openPremiumModal({ initialSection: 'saved_tags', }); return; } setLocalTextSearchTag({ tag }); runDebouncedForSearch(searchTextMessagesLocal); }); const handleUp = useLastCallback(() => { if (chat && foundIds) { const newFocusIndex = focusedIndex + 1; focusMessage({ chatId: chat.id, messageId: foundIds[newFocusIndex], threadId }); setFocusedIndex(newFocusIndex); } }); const handleDown = useLastCallback(() => { if (chat && foundIds) { const newFocusIndex = focusedIndex - 1; focusMessage({ chatId: chat.id, messageId: foundIds[newFocusIndex], threadId }); setFocusedIndex(newFocusIndex); } }); const handleCloseLocalTextSearch = useLastCallback(() => { closeLocalTextSearch(); }); return (
{hasTags && (
{tags.map((tag) => ( ))}
)}
{hasQueryData ? ( foundIds?.length ? ( `${focusedIndex + 1} of ${totalCount}` ) : foundIds && !foundIds.length ? ( 'No results' ) : ( '' ) ) : ( )}
); }; export default memo(withGlobal( (global): StateProps => { const currentMessageList = selectCurrentMessageList(global); if (!currentMessageList) { return {}; } const { chatId, threadId } = currentMessageList; const chat = selectChat(global, chatId); if (!chat) { return {}; } const { query, savedTag, results } = selectCurrentTextSearch(global) || {}; const { totalCount, foundIds } = results || {}; const isSavedMessages = selectIsChatWithSelf(global, chatId); const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId); const savedTags = isSavedMessages && !isSavedDialog ? global.savedReactionTags?.byKey : undefined; return { chat, query, totalCount, threadId, foundIds, isHistoryCalendarOpen: Boolean(selectTabState(global).historyCalendarSelectedAt), savedTags, searchTag: savedTag, isCurrentUserPremium: selectIsCurrentUserPremium(global), }; }, )(MobileSearchFooter));