diff --git a/src/components/left/main/LeftMainHeader.tsx b/src/components/left/main/LeftMainHeader.tsx index a1ec2df39..cb39d4d96 100644 --- a/src/components/left/main/LeftMainHeader.tsx +++ b/src/components/left/main/LeftMainHeader.tsx @@ -116,7 +116,7 @@ const LeftMainHeader: FC = ({ const oldLang = useOldLang(); const lang = useLang(); - const { isMobile } = useAppLayout(); + const { isMobile, isDesktop } = useAppLayout(); const [isBotMenuOpen, markBotMenuOpen, unmarkBotMenuOpen] = useFlag(); @@ -189,7 +189,7 @@ const LeftMainHeader: FC = ({ lockScreen(); }); - const isSearchFocused = ( + const isSearchFocused = (!isDesktop && !isMessageListOpen) && ( Boolean(globalSearchChatId) || content === LeftColumnContent.GlobalSearch || content === LeftColumnContent.Contacts diff --git a/src/components/left/settings/SettingsExperimental.tsx b/src/components/left/settings/SettingsExperimental.tsx index 354fbee50..c08232690 100644 --- a/src/components/left/settings/SettingsExperimental.tsx +++ b/src/components/left/settings/SettingsExperimental.tsx @@ -71,6 +71,10 @@ const SettingsExperimental: FC = ({ requestWave({ startX: e.clientX, startY: e.clientY }); }); + const handleRequestConfetti = useLastCallback(() => { + requestConfetti({ withStars: true }); + }); + const handleSnap = useLastCallback(() => { const button = snapButtonRef.current; if (!button) return; @@ -98,8 +102,7 @@ const SettingsExperimental: FC = ({
requestConfetti({ withStars: true })} + onClick={handleRequestConfetti} icon="animations" >
Launch some confetti!
diff --git a/src/components/middle/HeaderMenuContainer.tsx b/src/components/middle/HeaderMenuContainer.tsx index 60338f691..2403502ab 100644 --- a/src/components/middle/HeaderMenuContainer.tsx +++ b/src/components/middle/HeaderMenuContainer.tsx @@ -24,13 +24,13 @@ import { } from '../../global/helpers'; import { selectBot, + selectCanGift, selectCanManage, selectCanTranslateChat, selectChat, selectChatFullInfo, selectCurrentMessageList, selectIsChatWithSelf, - selectIsPremiumPurchaseBlocked, selectIsRightColumnShown, selectNotifyExceptions, selectNotifySettings, @@ -743,7 +743,7 @@ export default memo(withGlobal( const userFullInfo = isPrivate ? selectUserFullInfo(global, chatId) : undefined; const chatFullInfo = !isPrivate ? selectChatFullInfo(global, chatId) : undefined; const fullInfo = userFullInfo || chatFullInfo; - const canGift = !selectIsPremiumPurchaseBlocked(global) && !isChatWithSelf && isPrivate; + const canGift = selectCanGift(global, chatId); const topic = selectTopic(global, chatId, threadId); const canCreateTopic = chat.isForum && ( diff --git a/src/components/middle/hooks/useHeaderPane.tsx b/src/components/middle/hooks/useHeaderPane.tsx index 99e01bb1f..9f2d83cc2 100644 --- a/src/components/middle/hooks/useHeaderPane.tsx +++ b/src/components/middle/hooks/useHeaderPane.tsx @@ -39,7 +39,7 @@ export default function useHeaderPane void; }) { - const [shouldRender, setShouldRender] = useState(true); + const [shouldRender, setShouldRender] = useState(isOpen); // eslint-disable-next-line no-null/no-null const localRef = useRef(null); const ref = providedRef || localRef; diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index 0aee5d3e7..37a8cbf05 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -772,6 +772,7 @@ const Message: FC = ({ hasThread: hasThread && !noComments, forceSenderName, hasCommentCounter: hasThread && repliesThreadInfo.messagesCount > 0, + hasCommentButton: withCommentButton, hasActionButton: canForward || canFocus, hasReactions, isGeoLiveActive: location?.mediaType === 'geoLive' && !isGeoLiveExpired(message), diff --git a/src/components/middle/message/helpers/buildContentClassName.ts b/src/components/middle/message/helpers/buildContentClassName.ts index 9bb922da6..90fdd678c 100644 --- a/src/components/middle/message/helpers/buildContentClassName.ts +++ b/src/components/middle/message/helpers/buildContentClassName.ts @@ -17,6 +17,7 @@ export function buildContentClassName( hasThread, forceSenderName, hasCommentCounter, + hasCommentButton, hasActionButton, hasReactions, isGeoLiveActive, @@ -32,6 +33,7 @@ export function buildContentClassName( hasThread?: boolean; forceSenderName?: boolean; hasCommentCounter?: boolean; + hasCommentButton?: boolean; hasActionButton?: boolean; hasReactions?: boolean; isGeoLiveActive?: boolean; @@ -190,7 +192,7 @@ export function buildContentClassName( classNames.push('has-fact-check'); } - if (isLastInGroup && !hasInlineKeyboard && (photo || !isMediaWithNoText || (location && asForwarded))) { + if (isLastInGroup && !hasInlineKeyboard && (photo || !isMediaWithNoText || hasCommentButton)) { classNames.push('has-appendix'); } } diff --git a/src/components/middle/panes/ChatReportPane.tsx b/src/components/middle/panes/ChatReportPane.tsx index c790e87e5..215b154ef 100644 --- a/src/components/middle/panes/ChatReportPane.tsx +++ b/src/components/middle/panes/ChatReportPane.tsx @@ -2,12 +2,13 @@ import type { FC } from '../../../lib/teact/teact'; import React, { memo, useState } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; -import type { ApiChat, ApiUser } from '../../../api/types'; +import type { ApiPeer } from '../../../api/types'; import { getChatTitle, getUserFirstOrLastName, getUserFullName, isChatBasicGroup, } from '../../../global/helpers'; -import { selectChat, selectUser } from '../../../global/selectors'; +import { isApiPeerChat, isApiPeerUser } from '../../../global/helpers/peers'; +import { selectPeer } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev'; @@ -35,8 +36,7 @@ type OwnProps = { type StateProps = { currentUserId?: string; - chat?: ApiChat; - user?: ApiUser; + peer?: ApiPeer; }; const ChatReportPane: FC = ({ @@ -46,8 +46,7 @@ const ChatReportPane: FC = ({ canReportSpam, canAddContact, canBlockContact, - chat, - user, + peer, currentUserId, onPaneStateChange, }) => { @@ -67,6 +66,10 @@ const ChatReportPane: FC = ({ const [isBlockUserModalOpen, openBlockUserModal, closeBlockUserModal] = useFlag(); const [shouldReportSpam, setShouldReportSpam] = useState(true); const [shouldDeleteChat, setShouldDeleteChat] = useState(true); + + const renderingPeer = useCurrentOrPrev(peer); + const chat = renderingPeer && isApiPeerChat(renderingPeer) ? renderingPeer : undefined; + const user = renderingPeer && isApiPeerUser(renderingPeer) ? renderingPeer : undefined; const isBasicGroup = chat && isChatBasicGroup(chat); const renderingCanAddContact = useCurrentOrPrev(canAddContact); @@ -109,7 +112,7 @@ const ChatReportPane: FC = ({ const hasAnyButton = canAddContact || canBlockContact || canReportSpam; - const isRendering = Boolean(hasAnyButton && (chat || user)); + const isRendering = Boolean(hasAnyButton && peer); const { ref, shouldRender } = useHeaderPane({ isOpen: isRendering, @@ -202,7 +205,6 @@ const ChatReportPane: FC = ({ export default memo(withGlobal( (global, { chatId }): StateProps => ({ currentUserId: global.currentUserId, - chat: selectChat(global, chatId), - user: selectUser(global, chatId), + peer: selectPeer(global, chatId), }), )(ChatReportPane)); diff --git a/src/components/middle/search/MiddleSearch.tsx b/src/components/middle/search/MiddleSearch.tsx index 54f8f0d23..3e60285b4 100644 --- a/src/components/middle/search/MiddleSearch.tsx +++ b/src/components/middle/search/MiddleSearch.tsx @@ -129,6 +129,7 @@ const MiddleSearch: FC = ({ const inputRef = useRef(null); // eslint-disable-next-line no-null/no-null const containerRef = useRef(null); + const shouldCancelSearchRef = useRef(false); const { isMobile } = useAppLayout(); const oldLang = useOldLang(); @@ -215,14 +216,10 @@ const MiddleSearch: FC = ({ }; }, []); - // Focus message + // Reset focus on query result useEffect(() => { - if (foundIds?.length) { - setFocusedIndex(0); - } else { - setFocusedIndex(-1); - } - }, [searchType, focusMessage, foundIds, threadId]); + setFocusedIndex(-1); + }, [lastSearchQuery]); // Disable native up/down buttons on iOS useLayoutEffect(() => { @@ -309,10 +306,15 @@ const MiddleSearch: FC = ({ return; } - runDebouncedForSearch(() => performMiddleSearch({ chatId, threadId, query })); + runDebouncedForSearch(() => { + if (shouldCancelSearchRef.current) return; + performMiddleSearch({ chatId, threadId, query }); + }); }); const handleQueryChange = useLastCallback((newQuery: string) => { + shouldCancelSearchRef.current = false; + if (newQuery.startsWith('#') && !isHashtagQuery) { updateMiddleSearch({ chatId: chat!.id, threadId, update: { isHashtag: true } }); setQuery(newQuery.slice(1)); @@ -325,6 +327,7 @@ const MiddleSearch: FC = ({ if (!newQuery) { setIsLoading(false); resetMiddleSearch(); + shouldCancelSearchRef.current = true; } }); @@ -721,7 +724,7 @@ const MiddleSearch: FC = ({
{hasQueryData && ( foundIds?.length ? ( - oldLang('Of', [focusedIndex + 1, totalCount]) + oldLang('Of', [Math.max(focusedIndex + 1, 1), totalCount]) ) : foundIds && !foundIds.length && ( oldLang('NoResult') ) diff --git a/src/components/ui/Notification.scss b/src/components/ui/Notification.scss index 04fceed31..3c35b31a7 100644 --- a/src/components/ui/Notification.scss +++ b/src/components/ui/Notification.scss @@ -3,7 +3,6 @@ width: 22rem; max-width: 100vw; margin: 4.25rem auto 0.25rem; - margin-top: calc(4.25rem + var(--middle-header-panes-height)); z-index: var(--z-notification); & ~ & { diff --git a/src/global/actions/api/middleSearch.ts b/src/global/actions/api/middleSearch.ts index 6b308d7e2..926ed5038 100644 --- a/src/global/actions/api/middleSearch.ts +++ b/src/global/actions/api/middleSearch.ts @@ -134,7 +134,7 @@ addActionHandler('performMiddleSearch', async (global, actions, payload): Promis currentSearch = selectCurrentMiddleSearch(global, tabId); const hasTagChanged = currentSearch?.savedTag && !isSameReaction(savedTag, currentSearch.savedTag); - const hasSearchChanged = currentSearch?.fetchingQuery && currentSearch.fetchingQuery !== query; + const hasSearchChanged = currentSearch?.fetchingQuery !== query; if (!currentSearch || hasSearchChanged || hasTagChanged) { return; } diff --git a/src/global/selectors/users.ts b/src/global/selectors/users.ts index cd556e41e..651e4ff7d 100644 --- a/src/global/selectors/users.ts +++ b/src/global/selectors/users.ts @@ -3,6 +3,7 @@ import type { } from '../../api/types'; import type { GlobalState } from '../types'; +import { SERVICE_NOTIFICATIONS_USER_ID } from '../../config'; import { isUserBot } from '../helpers'; export function selectUser(global: T, userId: string): ApiUser | undefined { @@ -41,7 +42,9 @@ export function selectIsGiveawayGiftsPurchaseAvailable(gl return global.appConfig?.isGiveawayGiftsPurchaseAvailable ?? true; } -// Slow, not to be used in `withGlobal` +/** + * Slow, not to be used in `withGlobal` + */ export function selectUserByPhoneNumber(global: T, phoneNumber: string) { const phoneNumberCleaned = phoneNumber.replace(/[^0-9]/g, ''); @@ -56,3 +59,10 @@ export function selectBot(global: T, userId: string): Api return user; } + +export function selectCanGift(global: T, userId: string) { + const bot = selectBot(global, userId); + const user = selectUser(global, userId); + + return !selectIsPremiumPurchaseBlocked(global) && !bot && !user?.isSelf && userId !== SERVICE_NOTIFICATIONS_USER_ID; +}