From d3b4c57aab0fc8c28257306e6e36647024a02cea Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 19 Sep 2025 14:35:05 +0200 Subject: [PATCH] Follow up gifts and stories collections (#6216) --- src/components/common/AnimatedTabList.tsx | 4 +-- src/components/right/Profile.scss | 4 +-- src/components/right/Profile.tsx | 26 ++++++++++++------- .../right/gifts/StarGiftCollectionList.tsx | 12 ++++----- .../right/stories/StoryAlbumList.tsx | 10 +++---- src/global/actions/api/stars.ts | 10 +++---- src/global/actions/api/stories.ts | 6 ++--- src/global/actions/ui/payments.ts | 6 ++--- src/global/reducers/users.ts | 4 +-- src/global/selectors/payments.ts | 8 +++--- src/global/selectors/peers.ts | 3 ++- src/global/selectors/stories.ts | 8 ++++++ src/global/types/tabState.ts | 3 ++- 13 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/components/common/AnimatedTabList.tsx b/src/components/common/AnimatedTabList.tsx index 24b259435..d1833cd7a 100644 --- a/src/components/common/AnimatedTabList.tsx +++ b/src/components/common/AnimatedTabList.tsx @@ -23,14 +23,14 @@ type OwnProps = { items: TabItem[]; selectedItemId?: string; className?: string; - animationLevel?: AnimationLevel; + animationLevel: AnimationLevel; onItemSelect?: (itemId: string) => void; }; const AnimatedTabList = ({ items, selectedItemId, - animationLevel = 1, + animationLevel, onItemSelect, className, }: OwnProps) => { diff --git a/src/components/right/Profile.scss b/src/components/right/Profile.scss index 7a82f9090..ecfd2ec95 100644 --- a/src/components/right/Profile.scss +++ b/src/components/right/Profile.scss @@ -106,7 +106,7 @@ } .content { - transition: transform 0.3s; + transition: transform var(--layer-transition); &.showContentPanel { transform: translateY(3rem); padding-bottom: 3.5rem !important; @@ -230,7 +230,7 @@ right: 0; left: 0; - transition: transform 0.3s, opacity 0.3s; + transition: transform var(--layer-transition), opacity 0.2s ease; &.hiddenPanel { transform: translateY(-100%); diff --git a/src/components/right/Profile.tsx b/src/components/right/Profile.tsx index 732f4e43e..348dc3b53 100644 --- a/src/components/right/Profile.tsx +++ b/src/components/right/Profile.tsx @@ -14,6 +14,7 @@ import type { ApiUser, ApiUserStatus, } from '../../api/types'; +import type { ProfileCollectionKey } from '../../global/selectors/payments'; import type { TabState } from '../../global/types'; import type { AnimationLevel, ProfileState, ProfileTabType, SharedMediaType, ThemeKey, ThreadId } from '../../types'; import type { RegularLangKey } from '../../types/language'; @@ -21,6 +22,7 @@ import { MAIN_THREAD_ID } from '../../api/types'; import { AudioOrigin, MediaViewerOrigin, NewChatMembersProgress } from '../../types'; import { MEMBERS_SLICE, PROFILE_SENSITIVE_AREA, SHARED_MEDIA_SLICE, SLIDE_TRANSITION_DURATION } from '../../config'; +import { selectActiveGiftsCollectionId } from '../../global/selectors/payments'; const CONTENT_PANEL_SHOW_DELAY = 300; import { @@ -57,6 +59,7 @@ import { import { selectPremiumLimit } from '../../global/selectors/limits'; import { selectMessageDownloadableMedia } from '../../global/selectors/media'; import { selectSharedSettings } from '../../global/selectors/sharedState'; +import { selectActiveStoriesCollectionId } from '../../global/selectors/stories'; import { areDeepEqual } from '../../util/areDeepEqual'; import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment'; import buildClassName from '../../util/buildClassName'; @@ -147,8 +150,8 @@ type StateProps = { pinnedStoryIds?: number[]; archiveStoryIds?: number[]; storyByIds?: Record; - selectedStoryAlbumId?: number; - activeCollectionId?: number; + selectedStoryAlbumId: ProfileCollectionKey; + activeCollectionId: ProfileCollectionKey; giftsFilter?: any; chatsById: Record; usersById: Record; @@ -613,10 +616,12 @@ const Profile: FC = ({ if (isFirstTab) { renderingDelay = !isRightColumnShown ? HIDDEN_RENDER_DELAY : 0; // @optimization Used to delay first render of secondary tabs while animating - } else if (!viewportIds && !botPreviewMedia) { + } else if ((!viewportIds && !botPreviewMedia) || (!gifts?.length && resultType === 'gifts')) { renderingDelay = SLIDE_TRANSITION_DURATION; } - const canRenderContent = useAsyncRendering([chatId, threadId, resultType, renderingActiveTab], renderingDelay); + + const canRenderContent = useAsyncRendering([chatId, threadId, resultType, + renderingActiveTab, activeCollectionId, selectedStoryAlbumId], renderingDelay); function getMemberContextAction(memberId: string): MenuItemContextAction[] | undefined { return memberId === currentUserId || !canDeleteMembers ? undefined : [{ @@ -986,10 +991,10 @@ const Profile: FC = ({ const shouldUseTransitionForContent = resultType === 'stories' || resultType === 'gifts'; const contentTransitionKey = (() => { if (resultType === 'stories') { - return selectedStoryAlbumId || 0; + return selectedStoryAlbumId === 'all' ? 0 : selectedStoryAlbumId; } if (resultType === 'gifts') { - return activeCollectionId || 0; + return activeCollectionId === 'all' ? 0 : activeCollectionId; } return 0; })(); @@ -1178,8 +1183,9 @@ export default memo(withGlobal( && !isSavedDialog; const peerStories = hasStoriesTab ? selectPeerStories(global, peer.id) : undefined; const tabState = selectTabState(global); - const { selectedStoryAlbumId, nextProfileTab, forceScrollProfileTab, savedGifts } = tabState; - const storyIds = selectedStoryAlbumId + const { nextProfileTab, forceScrollProfileTab, savedGifts } = tabState; + const selectedStoryAlbumId = selectActiveStoriesCollectionId(global); + const storyIds = selectedStoryAlbumId !== 'all' ? peerStories?.idsByAlbumId?.[selectedStoryAlbumId]?.ids : peerStories?.profileIds; const pinnedStoryIds = peerStories?.pinnedIds; @@ -1187,8 +1193,8 @@ export default memo(withGlobal( const archiveStoryIds = peerStories?.archiveIds; const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedDialog; - const activeCollectionId = savedGifts.activeCollectionByPeerId[chatId]; - const peerGifts = savedGifts.collectionsByPeerId[chatId]?.[activeCollectionId || 'all']; + const activeCollectionId = selectActiveGiftsCollectionId(global, chatId); + const peerGifts = savedGifts.collectionsByPeerId[chatId]?.[activeCollectionId]; const storyAlbums = global.stories.albumsByPeerId?.[chatId]; const giftCollections = global.starGiftCollections?.byPeerId?.[chatId]; diff --git a/src/components/right/gifts/StarGiftCollectionList.tsx b/src/components/right/gifts/StarGiftCollectionList.tsx index 670730b44..ba0a77807 100644 --- a/src/components/right/gifts/StarGiftCollectionList.tsx +++ b/src/components/right/gifts/StarGiftCollectionList.tsx @@ -2,10 +2,11 @@ import { memo, useMemo } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiStarGiftCollection } from '../../../api/types'; +import type { ProfileCollectionKey } from '../../../global/selectors/payments'; import type { AnimationLevel } from '../../../types'; import type { TabItem } from '../../common/AnimatedTabList'; -import { selectActiveCollectionId } from '../../../global/selectors'; +import { selectActiveGiftsCollectionId } from '../../../global/selectors'; import { selectSharedSettings } from '../../../global/selectors/sharedState'; import buildClassName from '../../../util/buildClassName'; @@ -15,7 +16,6 @@ import useLastCallback from '../../../hooks/useLastCallback'; import AnimatedTabList from '../../common/AnimatedTabList'; import styles from './StarGiftCollectionList.module.scss'; - type OwnProps = { peerId: string; className?: string; @@ -23,8 +23,8 @@ type OwnProps = { type StateProps = { collections?: ApiStarGiftCollection[]; - activeCollectionId?: number; - animationLevel?: AnimationLevel; + activeCollectionId: ProfileCollectionKey; + animationLevel: AnimationLevel; }; const StarGiftCollectionList = ({ @@ -37,7 +37,7 @@ const StarGiftCollectionList = ({ const { updateSelectedGiftCollection, resetSelectedGiftCollection } = getActions(); const lang = useLang(); - const handleItemSelect = useLastCallback((itemId?: string) => { + const handleItemSelect = useLastCallback((itemId: string) => { if (itemId === 'all') { resetSelectedGiftCollection({ peerId }); } else { @@ -79,7 +79,7 @@ export default memo(withGlobal( (global, { peerId }): StateProps => { const { starGiftCollections } = global; const collections = starGiftCollections?.byPeerId?.[peerId]; - const activeCollectionId = selectActiveCollectionId(global, peerId); + const activeCollectionId = selectActiveGiftsCollectionId(global, peerId); return { collections, diff --git a/src/components/right/stories/StoryAlbumList.tsx b/src/components/right/stories/StoryAlbumList.tsx index 0361d6e7b..342db263f 100644 --- a/src/components/right/stories/StoryAlbumList.tsx +++ b/src/components/right/stories/StoryAlbumList.tsx @@ -2,11 +2,12 @@ import { memo, useMemo } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiStoryAlbum } from '../../../api/types'; +import type { ProfileCollectionKey } from '../../../global/selectors/payments'; import type { AnimationLevel } from '../../../types'; import type { TabItem } from '../../common/AnimatedTabList'; -import { selectTabState } from '../../../global/selectors'; import { selectSharedSettings } from '../../../global/selectors/sharedState'; +import { selectActiveStoriesCollectionId } from '../../../global/selectors/stories'; import buildClassName from '../../../util/buildClassName'; import useLang from '../../../hooks/useLang'; @@ -23,8 +24,8 @@ type OwnProps = { type StateProps = { albums?: ApiStoryAlbum[]; - selectedAlbumId?: number; - animationLevel?: AnimationLevel; + selectedAlbumId: ProfileCollectionKey; + animationLevel: AnimationLevel; }; const StoryAlbumList = ({ @@ -77,9 +78,8 @@ const StoryAlbumList = ({ export default memo(withGlobal( (global, { peerId }): StateProps => { const { stories } = global; - const tabState = selectTabState(global); const albums = stories?.albumsByPeerId?.[peerId]; - const selectedAlbumId = tabState.selectedStoryAlbumId; + const selectedAlbumId = selectActiveStoriesCollectionId(global); return { albums, diff --git a/src/global/actions/api/stars.ts b/src/global/actions/api/stars.ts index fb0c4c33f..6f1c8a608 100644 --- a/src/global/actions/api/stars.ts +++ b/src/global/actions/api/stars.ts @@ -25,7 +25,7 @@ import { } from '../../reducers'; import { updateTabState } from '../../reducers/tabs'; import { - selectActiveCollectionId, + selectActiveGiftsCollectionId, selectGiftProfileFilter, selectPeer, selectPeerSavedGifts, @@ -303,18 +303,18 @@ addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise if (!shouldRefresh && currentGifts && !localNextOffset) return; // Already loaded all const fetchingFilter = selectGiftProfileFilter(global, peerId, tabId); - const fetchingCollectionId = selectActiveCollectionId(global, peerId, tabId); + const fetchingCollectionId = selectActiveGiftsCollectionId(global, peerId, tabId); const result = await callApi('fetchSavedStarGifts', { peer, offset: !shouldRefresh ? localNextOffset : '', filter: fetchingFilter, - collectionId: fetchingCollectionId ? Number(fetchingCollectionId) : undefined, + collectionId: fetchingCollectionId === 'all' ? undefined : fetchingCollectionId, }); global = getGlobal(); const currentFilter = selectGiftProfileFilter(global, peerId, tabId); - const currentCollectionId = selectActiveCollectionId(global, peerId, tabId); + const currentCollectionId = selectActiveGiftsCollectionId(global, peerId, tabId); if (!result || currentCollectionId !== fetchingCollectionId || currentFilter !== fetchingFilter) { return; @@ -400,7 +400,7 @@ addActionHandler('changeGiftVisibility', async (global, actions, payload): Promi const requestInputGift = getRequestInputSavedStarGift(global, gift); if (!requestInputGift) return; - const activeCollectionId = selectActiveCollectionId(global, peerId, tabId) || 'all'; + const activeCollectionId = selectActiveGiftsCollectionId(global, peerId, tabId); const oldGifts = selectTabState(global, tabId).savedGifts.collectionsByPeerId[peerId]?.[activeCollectionId]; if (oldGifts?.gifts?.length) { const newGifts = oldGifts.gifts.map((g) => { diff --git a/src/global/actions/api/stories.ts b/src/global/actions/api/stories.ts index 812b06569..0b11c5c88 100644 --- a/src/global/actions/api/stories.ts +++ b/src/global/actions/api/stories.ts @@ -31,6 +31,7 @@ import { selectPeer, selectPeerStories, selectPeerStory, selectPinnedStories, selectTabState, } from '../../selectors'; +import { selectActiveStoriesCollectionId } from '../../selectors/stories'; const INFINITE_LOOP_MARKER = 100; @@ -308,9 +309,8 @@ addActionHandler('loadPeerProfileStories', async (global, actions, payload): Pro return; } - const tabState = selectTabState(global, tabId); - const selectedAlbumId = tabState.selectedStoryAlbumId; - if (selectedAlbumId) { + const selectedAlbumId = selectActiveStoriesCollectionId(global, tabId); + if (selectedAlbumId !== 'all') { let albumData = peerStories?.idsByAlbumId?.[selectedAlbumId]; if (albumData?.isFullyLoaded) { return; diff --git a/src/global/actions/ui/payments.ts b/src/global/actions/ui/payments.ts index 65aa65b59..eb39f35e1 100644 --- a/src/global/actions/ui/payments.ts +++ b/src/global/actions/ui/payments.ts @@ -2,7 +2,7 @@ import type { ApiSavedGifts } from '../../../api/types'; import type { ActionReturnType } from '../../types'; import { DEFAULT_GIFT_PROFILE_FILTER_OPTIONS } from '../../../config'; -import { selectActiveCollectionId } from '../../../global/selectors'; +import { selectActiveGiftsCollectionId } from '../../../global/selectors'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { addActionHandler, setGlobal } from '../../index'; import { @@ -102,7 +102,7 @@ addActionHandler('updateGiftProfileFilter', (global, actions, payload): ActionRe }; } - const activeCollectionId = selectActiveCollectionId(global, peerId, tabId) || 'all'; + const activeCollectionId = selectActiveGiftsCollectionId(global, peerId, tabId); global = updateTabState(global, { savedGifts: { @@ -126,7 +126,7 @@ addActionHandler('resetGiftProfileFilter', (global, actions, payload): ActionRet const { peerId, tabId = getCurrentTabId() } = payload || {}; const tabState = selectTabState(global, tabId); - const activeCollectionId = selectActiveCollectionId(global, peerId, tabId) || 'all'; + const activeCollectionId = selectActiveGiftsCollectionId(global, peerId, tabId); global = updateTabState(global, { savedGifts: { diff --git a/src/global/reducers/users.ts b/src/global/reducers/users.ts index 980fb6454..c77c614ba 100644 --- a/src/global/reducers/users.ts +++ b/src/global/reducers/users.ts @@ -15,7 +15,7 @@ import { getCurrentTabId } from '../../util/establishMultitabRole'; import { omit, omitUndefined, unique } from '../../util/iteratees'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; import { getSavedGiftKey } from '../helpers/stars'; -import { selectActiveCollectionId } from '../selectors'; +import { selectActiveGiftsCollectionId } from '../selectors'; import { selectTabState } from '../selectors'; import { updateTabState } from './tabs'; @@ -346,7 +346,7 @@ export function replacePeerSavedGifts( keyCounts.set(id, count + 1); }); - const activeCollectionId = selectActiveCollectionId(global, peerId, tabId) || 'all'; + const activeCollectionId = selectActiveGiftsCollectionId(global, peerId, tabId); return updateTabState(global, { savedGifts: { diff --git a/src/global/selectors/payments.ts b/src/global/selectors/payments.ts index 0d7ce978b..ba858b8aa 100644 --- a/src/global/selectors/payments.ts +++ b/src/global/selectors/payments.ts @@ -10,6 +10,8 @@ import { selectChat } from './chats'; import { selectTabState } from './tabs'; import { selectUser } from './users'; +export type ProfileCollectionKey = number | 'all'; + export function selectPaymentInputInvoice( global: T, ...[tabId = getCurrentTabId()]: TabArgs @@ -97,10 +99,10 @@ export function selectIsGiftProfileFilterDefault( return arePropsShallowEqual(selectTabState(global, tabId).savedGifts.filter, DEFAULT_GIFT_PROFILE_FILTER_OPTIONS); } -export function selectActiveCollectionId( +export function selectActiveGiftsCollectionId( global: T, peerId: string, ...[tabId = getCurrentTabId()]: TabArgs -) { - return selectTabState(global, tabId).savedGifts.activeCollectionByPeerId?.[peerId]; +): ProfileCollectionKey { + return selectTabState(global, tabId).savedGifts.activeCollectionByPeerId?.[peerId] || 'all'; } diff --git a/src/global/selectors/peers.ts b/src/global/selectors/peers.ts index 4a3012ce6..ef1b30a01 100644 --- a/src/global/selectors/peers.ts +++ b/src/global/selectors/peers.ts @@ -6,6 +6,7 @@ import { isUserId } from '../../util/entities/ids'; import { getCurrentTabId } from '../../util/establishMultitabRole'; import { isChatAdmin, isDeletedUser } from '../helpers'; import { selectChat, selectChatFullInfo } from './chats'; +import { selectActiveGiftsCollectionId } from './payments'; import { selectTabState } from './tabs'; import { selectBot, selectUser, selectUserFullInfo } from './users'; @@ -34,7 +35,7 @@ export function selectPeerSavedGifts( ...[tabId = getCurrentTabId()]: TabArgs ): ApiSavedGifts | undefined { const tabState = selectTabState(global, tabId); - const activeCollectionId = tabState.savedGifts.activeCollectionByPeerId[peerId] || 'all'; + const activeCollectionId = selectActiveGiftsCollectionId(global, peerId, tabId); return tabState.savedGifts.collectionsByPeerId[peerId]?.[activeCollectionId]; } diff --git a/src/global/selectors/stories.ts b/src/global/selectors/stories.ts index 960330307..c3c3f7c06 100644 --- a/src/global/selectors/stories.ts +++ b/src/global/selectors/stories.ts @@ -1,5 +1,6 @@ import type { ApiPeerStories, ApiTypeStory } from '../../api/types'; import type { GlobalState, TabArgs } from '../types'; +import type { ProfileCollectionKey } from './payments'; import { getCurrentTabId } from '../../util/establishMultitabRole'; import { selectPeer } from './peers'; @@ -150,3 +151,10 @@ function getPeerStoryIdsForViewer( ? storyIds.slice(lastReadIndex + 1) : undefined; } + +export function selectActiveStoriesCollectionId( + global: T, + ...[tabId = getCurrentTabId()]: TabArgs +): ProfileCollectionKey { + return selectTabState(global, tabId).selectedStoryAlbumId || 'all'; +} diff --git a/src/global/types/tabState.ts b/src/global/types/tabState.ts index 1f6ac82a8..158f2eb3f 100644 --- a/src/global/types/tabState.ts +++ b/src/global/types/tabState.ts @@ -94,6 +94,7 @@ import type { import type { WebApp, WebAppModalStateType } from '../../types/webapp'; import type { SearchResultKey } from '../../util/keys/searchResultKey'; import type { RegularLangFnParameters } from '../../util/localization'; +import type { ProfileCollectionKey } from '../selectors/payments'; import type { CallbackAction } from './actions'; export type TabState = { @@ -220,7 +221,7 @@ export type TabState = { }; savedGifts: { - collectionsByPeerId: Record>; + collectionsByPeerId: Record>; activeCollectionByPeerId: Record; filter: GiftProfileFilterOptions; };