From 81386ba911ec5d9382779af6caed29cb84efb233 Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Fri, 6 Dec 2024 19:43:53 +0400 Subject: [PATCH] Chat: Add mini app button (#5263) --- src/components/left/main/Chat.tsx | 9 ++-- src/components/left/main/ChatBadge.scss | 4 ++ src/components/left/main/ChatBadge.tsx | 42 ++++++++++++++++++- .../left/search/LeftSearchResultChat.tsx | 40 ++++++++++++++++-- src/components/left/search/RecentContacts.tsx | 1 + 5 files changed, 87 insertions(+), 9 deletions(-) diff --git a/src/components/left/main/Chat.tsx b/src/components/left/main/Chat.tsx index b9c05d468..87902ba91 100644 --- a/src/components/left/main/Chat.tsx +++ b/src/components/left/main/Chat.tsx @@ -21,7 +21,6 @@ import { StoryViewerOrigin } from '../../../types'; import { getMessageAction, - getPrivateChatUserId, groupStatetefulContent, isUserId, isUserOnline, @@ -367,6 +366,7 @@ const Chat: FC = ({ shouldShowOnlyMostImportant forceHidden={getIsForumPanelClosed} topics={topics} + isSelected={isSelected} /> {chat.isCallActive && chat.isCallNotEmpty && ( @@ -400,7 +400,9 @@ const Chat: FC = ({ isPinned={isPinned} isMuted={isMuted} isSavedDialog={isSavedDialog} + hasMiniApp={user?.hasMainMiniApp} topics={topics} + isSelected={isSelected} /> )} @@ -439,6 +441,7 @@ export default memo(withGlobal( chatId, isSavedDialog, isPreview, previewMessageId, }): StateProps => { const chat = selectChat(global, chatId); + const user = selectUser(global, chatId); if (!chat) { return { currentUserId: global.currentUserId!, @@ -458,7 +461,6 @@ export default memo(withGlobal( ? selectChatMessage(global, chat.id, replyToMessageId) : undefined; const { targetUserIds: actionTargetUserIds, targetChatId: actionTargetChatId } = lastMessageAction || {}; - const privateChatUserId = getPrivateChatUserId(chat); const { chatId: currentChatId, @@ -470,8 +472,7 @@ export default memo(withGlobal( const isSelectedForum = (chat.isForum && chatId === currentChatId) || chatId === selectTabState(global).forumPanelChatId; - const user = privateChatUserId ? selectUser(global, privateChatUserId) : undefined; - const userStatus = privateChatUserId ? selectUserStatus(global, privateChatUserId) : undefined; + const userStatus = selectUserStatus(global, chatId); const lastMessageTopic = lastMessage && selectTopicFromMessage(global, lastMessage); const typingStatus = selectThreadParam(global, chatId, MAIN_THREAD_ID, 'typingStatus'); diff --git a/src/components/left/main/ChatBadge.scss b/src/components/left/main/ChatBadge.scss index 8aba6f631..d7f69fa97 100644 --- a/src/components/left/main/ChatBadge.scss +++ b/src/components/left/main/ChatBadge.scss @@ -97,4 +97,8 @@ } } } + + &.miniapp { + z-index: calc(var(--z-chat-ripple) + 1); + } } diff --git a/src/components/left/main/ChatBadge.tsx b/src/components/left/main/ChatBadge.tsx index 6a83c52de..32d8a0353 100644 --- a/src/components/left/main/ChatBadge.tsx +++ b/src/components/left/main/ChatBadge.tsx @@ -1,5 +1,6 @@ import type { FC } from '../../../lib/teact/teact'; import React, { memo, useMemo } from '../../../lib/teact/teact'; +import { getActions } from '../../../global'; import type { ApiChat, ApiTopic } from '../../../api/types'; import type { Signal } from '../../../util/signals'; @@ -7,10 +8,14 @@ import type { Signal } from '../../../util/signals'; import buildClassName from '../../../util/buildClassName'; import { isSignal } from '../../../util/signals'; import { formatIntegerCompact } from '../../../util/textFormat'; +import { extractCurrentThemeParams } from '../../../util/themeStyle'; import useDerivedState from '../../../hooks/useDerivedState'; +import useLastCallback from '../../../hooks/useLastCallback'; +import useOldLang from '../../../hooks/useOldLang'; import AnimatedCounter from '../../common/AnimatedCounter'; +import Button from '../../ui/Button'; import ShowTransition from '../../ui/ShowTransition'; import './ChatBadge.scss'; @@ -23,8 +28,10 @@ type OwnProps = { isMuted?: boolean; isSavedDialog?: boolean; shouldShowOnlyMostImportant?: boolean; + hasMiniApp?: boolean; forceHidden?: boolean | Signal; topics?: Record; + isSelected?: boolean; }; const ChatBadge: FC = ({ @@ -37,7 +44,13 @@ const ChatBadge: FC = ({ wasTopicOpened, forceHidden, isSavedDialog, + hasMiniApp, + isSelected, }) => { + const { requestMainWebView } = getActions(); + + const oldLang = useOldLang(); + const { unreadMentionsCount = 0, unreadReactionsCount = 0, } = !chat.isForum ? chat : {}; // TODO[forums] Unread mentions and reactions temporarily disabled for forums @@ -71,7 +84,7 @@ const ChatBadge: FC = ({ ); const isShown = !resolvedForceHidden && Boolean( unreadCount || unreadMentionsCount || hasUnreadMark || isPinned || unreadReactionsCount - || isTopicUnopened, + || isTopicUnopened || hasMiniApp, ); const isUnread = Boolean((unreadCount || hasUnreadMark) && !isSavedDialog); @@ -82,6 +95,18 @@ const ChatBadge: FC = ({ isUnread && 'unread', ); + const handleOpenApp = useLastCallback((e: React.MouseEvent) => { + e.stopPropagation(); + + const theme = extractCurrentThemeParams(); + requestMainWebView({ + botId: chat.id, + peerId: chat.id, + theme, + shouldMarkBotTrusted: true, + }); + }); + function renderContent() { const unreadReactionsElement = unreadReactionsCount && (
@@ -111,6 +136,18 @@ const ChatBadge: FC = ({
); + const miniAppButton = hasMiniApp && ( + + ); + const visiblePinnedElement = !unreadCountElement && !unreadMentionsElement && !unreadReactionsElement && pinnedElement; @@ -120,6 +157,9 @@ const ChatBadge: FC = ({ if (isSavedDialog) return pinnedElement; + // Show only if empty or have pinned icon + if (hasMiniApp && (elements.length === 0 || visiblePinnedElement)) return miniAppButton; + if (elements.length === 0) return undefined; if (elements.length === 1) return elements[0]; diff --git a/src/components/left/search/LeftSearchResultChat.tsx b/src/components/left/search/LeftSearchResultChat.tsx index e36d714a5..561be226c 100644 --- a/src/components/left/search/LeftSearchResultChat.tsx +++ b/src/components/left/search/LeftSearchResultChat.tsx @@ -1,6 +1,6 @@ import type { FC } from '../../../lib/teact/teact'; import React, { memo, useCallback } from '../../../lib/teact/teact'; -import { withGlobal } from '../../../global'; +import { getActions, withGlobal } from '../../../global'; import type { ApiChat, ApiUser } from '../../../api/types'; import { StoryViewerOrigin } from '../../../types'; @@ -10,13 +10,17 @@ import { selectChat, selectIsChatPinned, selectNotifyExceptions, selectNotifySettings, selectUser, } from '../../../global/selectors'; +import { extractCurrentThemeParams } from '../../../util/themeStyle'; import useChatContextActions from '../../../hooks/useChatContextActions'; import useFlag from '../../../hooks/useFlag'; +import useLastCallback from '../../../hooks/useLastCallback'; +import useOldLang from '../../../hooks/useOldLang'; import useSelectWithEnter from '../../../hooks/useSelectWithEnter'; import GroupChatInfo from '../../common/GroupChatInfo'; import PrivateChatInfo from '../../common/PrivateChatInfo'; +import Button from '../../ui/Button'; import ListItem from '../../ui/ListItem'; import ChatFolderModal from '../ChatFolderModal.async'; import MuteChatModal from '../MuteChatModal.async'; @@ -24,6 +28,7 @@ import MuteChatModal from '../MuteChatModal.async'; type OwnProps = { chatId: string; withUsername?: boolean; + isRecent?: boolean; onClick: (id: string) => void; }; @@ -38,13 +43,17 @@ type StateProps = { const LeftSearchResultChat: FC = ({ chatId, withUsername, - onClick, chat, user, isPinned, isMuted, canChangeFolder, + isRecent, + onClick, }) => { + const { requestMainWebView } = getActions(); + const oldLang = useOldLang(); + const [isMuteModalOpen, openMuteModal, closeMuteModal] = useFlag(); const [isChatFolderModalOpen, openChatFolderModal, closeChatFolderModal] = useFlag(); const [shouldRenderChatFolderModal, markRenderChatFolderModal, unmarkRenderChatFolderModal] = useFlag(); @@ -70,9 +79,21 @@ const LeftSearchResultChat: FC = ({ handleChatFolderChange, }, true); - const handleClick = useCallback(() => { + const handleClick = useLastCallback(() => { onClick(chatId); - }, [chatId, onClick]); + }); + + const handleOpenApp = useLastCallback((e: React.MouseEvent) => { + e.stopPropagation(); + + const theme = extractCurrentThemeParams(); + requestMainWebView({ + botId: chatId, + peerId: chatId, + theme, + shouldMarkBotTrusted: true, + }); + }); const buttonRef = useSelectWithEnter(handleClick); @@ -100,6 +121,17 @@ const LeftSearchResultChat: FC = ({ storyViewerOrigin={StoryViewerOrigin.SearchResult} /> )} + {isRecent && user?.hasMainMiniApp && ( + + )} {shouldRenderMuteModal && ( = ({ {recentlyFoundChatIds.map((id) => ( ))}