Gifts Filter: Support transition for gifts in profile (#5584)

This commit is contained in:
Alexander Zinchuk 2025-02-13 14:28:13 +01:00
parent 8922beda37
commit ae4fc89058
6 changed files with 77 additions and 22 deletions

View File

@ -131,6 +131,7 @@ type StateProps = {
hasPreviewMediaTab?: boolean; hasPreviewMediaTab?: boolean;
hasGiftsTab?: boolean; hasGiftsTab?: boolean;
gifts?: ApiSavedStarGift[]; gifts?: ApiSavedStarGift[];
giftsTransitionKey: number;
areMembersHidden?: boolean; areMembersHidden?: boolean;
canAddMembers?: boolean; canAddMembers?: boolean;
canDeleteMembers?: boolean; canDeleteMembers?: boolean;
@ -199,6 +200,7 @@ const Profile: FC<OwnProps & StateProps> = ({
hasPreviewMediaTab, hasPreviewMediaTab,
hasGiftsTab, hasGiftsTab,
gifts, gifts,
giftsTransitionKey,
botPreviewMedia, botPreviewMedia,
areMembersHidden, areMembersHidden,
canAddMembers, canAddMembers,
@ -490,7 +492,7 @@ const Profile: FC<OwnProps & StateProps> = ({
}, [hasMembersTab, activeTab, tabs]); }, [hasMembersTab, activeTab, tabs]);
const handleResetGiftsFilter = useLastCallback(() => { const handleResetGiftsFilter = useLastCallback(() => {
resetGiftProfileFilter(); resetGiftProfileFilter({ peerId: chatId });
}); });
useEffect(() => { useEffect(() => {
@ -827,6 +829,20 @@ const Profile: FC<OwnProps & StateProps> = ({
); );
} }
const shouldUseTransitionForContent = resultType === 'gifts';
const contentTransitionKey = giftsTransitionKey;
function renderContentWithTransition() {
return (
<Transition
activeKey={contentTransitionKey}
name="fade"
>
{renderContent()}
</Transition>
);
}
return ( return (
<InfiniteScroll <InfiniteScroll
ref={containerRef} ref={containerRef}
@ -859,7 +875,7 @@ const Profile: FC<OwnProps & StateProps> = ({
onStart={applyTransitionFix} onStart={applyTransitionFix}
onStop={handleTransitionStop} onStop={handleTransitionStop}
> >
{renderContent()} {shouldUseTransitionForContent ? renderContentWithTransition() : renderContent()}
</Transition> </Transition>
<TabList activeTab={renderingActiveTab} tabs={tabs} onSwitchTab={handleSwitchTab} /> <TabList activeTab={renderingActiveTab} tabs={tabs} onSwitchTab={handleSwitchTab} />
</div> </div>
@ -952,6 +968,7 @@ export default memo(withGlobal<OwnProps>(
const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedDialog; const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedDialog;
const peerGifts = selectTabState(global).savedGifts.giftsByPeerId[chatId]; const peerGifts = selectTabState(global).savedGifts.giftsByPeerId[chatId];
const giftsTransitionKey = selectTabState(global).savedGifts.transitionKey || 0;
const isNotDefaultGiftFilter = !selectIsGiftProfileFilterDefault(global); const isNotDefaultGiftFilter = !selectIsGiftProfileFilterDefault(global);
@ -979,6 +996,7 @@ export default memo(withGlobal<OwnProps>(
storyIds, storyIds,
hasGiftsTab, hasGiftsTab,
gifts: peerGifts?.gifts, gifts: peerGifts?.gifts,
giftsTransitionKey,
pinnedStoryIds, pinnedStoryIds,
archiveStoryIds, archiveStoryIds,
storyByIds, storyByIds,

View File

@ -509,7 +509,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
return ( return (
<> <>
<h3 className="title">{lang('ProfileTabGifts')}</h3> <h3 className="title">{lang('ProfileTabGifts')}</h3>
{canUseGiftFilter && ( {canUseGiftFilter && chatId && (
<section className="tools"> <section className="tools">
<DropdownMenu <DropdownMenu
trigger={PrimaryLinkMenuButton} trigger={PrimaryLinkMenuButton}
@ -520,7 +520,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
icon={giftsSortType === 'byDate' ? 'calendar-filter' : 'cash-circle'} icon={giftsSortType === 'byDate' ? 'calendar-filter' : 'cash-circle'}
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
onClick={() => updateGiftProfileFilter( onClick={() => updateGiftProfileFilter(
{ filter: { sortType: giftsSortType === 'byDate' ? 'byValue' : 'byDate' } }, { peerId: chatId, filter: { sortType: giftsSortType === 'byDate' ? 'byValue' : 'byDate' } },
)} )}
> >
{lang(giftsSortType === 'byDate' ? 'GiftSortByDate' : 'GiftSortByValue')} {lang(giftsSortType === 'byDate' ? 'GiftSortByDate' : 'GiftSortByValue')}
@ -532,7 +532,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
icon={shouldIncludeUnlimitedGifts ? 'check' : 'placeholder'} icon={shouldIncludeUnlimitedGifts ? 'check' : 'placeholder'}
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
onClick={() => updateGiftProfileFilter( onClick={() => updateGiftProfileFilter(
{ filter: { shouldIncludeUnlimited: !shouldIncludeUnlimitedGifts } }, { peerId: chatId, filter: { shouldIncludeUnlimited: !shouldIncludeUnlimitedGifts } },
)} )}
> >
{lang('GiftFilterUnlimited')} {lang('GiftFilterUnlimited')}
@ -542,7 +542,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
icon={shouldIncludeLimitedGifts ? 'check' : 'placeholder'} icon={shouldIncludeLimitedGifts ? 'check' : 'placeholder'}
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
onClick={() => updateGiftProfileFilter( onClick={() => updateGiftProfileFilter(
{ filter: { shouldIncludeLimited: !shouldIncludeLimitedGifts } }, { peerId: chatId, filter: { shouldIncludeLimited: !shouldIncludeLimitedGifts } },
)} )}
> >
{lang('GiftFilterLimited')} {lang('GiftFilterLimited')}
@ -552,7 +552,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
icon={shouldIncludeUniqueGifts ? 'check' : 'placeholder'} icon={shouldIncludeUniqueGifts ? 'check' : 'placeholder'}
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
onClick={() => updateGiftProfileFilter( onClick={() => updateGiftProfileFilter(
{ filter: { shouldIncludeUnique: !shouldIncludeUniqueGifts } }, { peerId: chatId, filter: { shouldIncludeUnique: !shouldIncludeUniqueGifts } },
)} )}
> >
{lang('GiftFilterUnique')} {lang('GiftFilterUnique')}
@ -565,7 +565,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
icon={shouldIncludeDisplayedGifts ? 'check' : 'placeholder'} icon={shouldIncludeDisplayedGifts ? 'check' : 'placeholder'}
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
onClick={() => updateGiftProfileFilter( onClick={() => updateGiftProfileFilter(
{ filter: { shouldIncludeDisplayed: !shouldIncludeDisplayedGifts } }, { peerId: chatId, filter: { shouldIncludeDisplayed: !shouldIncludeDisplayedGifts } },
)} )}
> >
{lang('GiftFilterDisplayed')} {lang('GiftFilterDisplayed')}
@ -575,7 +575,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
icon={shouldIncludeHiddenGifts ? 'check' : 'placeholder'} icon={shouldIncludeHiddenGifts ? 'check' : 'placeholder'}
// eslint-disable-next-line react/jsx-no-bind // eslint-disable-next-line react/jsx-no-bind
onClick={() => updateGiftProfileFilter( onClick={() => updateGiftProfileFilter(
{ filter: { shouldIncludeHidden: !shouldIncludeHiddenGifts } }, { peerId: chatId, filter: { shouldIncludeHidden: !shouldIncludeHiddenGifts } },
)} )}
> >
{lang('GiftFilterHidden')} {lang('GiftFilterHidden')}

View File

@ -138,7 +138,7 @@ addActionHandler('loadStarGifts', async (global): Promise<void> => {
addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise<void> => { addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise<void> => {
const { const {
peerId, shouldRefresh, tabId = getCurrentTabId(), peerId, shouldRefresh, withTransition, tabId = getCurrentTabId(),
} = payload; } = payload;
const peer = selectPeer(global, peerId); const peer = selectPeer(global, peerId);
@ -149,20 +149,35 @@ addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise
if (!shouldRefresh && currentGifts && !localNextOffset) return; // Already loaded all if (!shouldRefresh && currentGifts && !localNextOffset) return; // Already loaded all
global = getGlobal();
const fetchingFilter = selectGiftProfileFilter(global, peerId, tabId);
const result = await callApi('fetchSavedStarGifts', { const result = await callApi('fetchSavedStarGifts', {
peer, peer,
offset: !shouldRefresh ? localNextOffset : '', offset: !shouldRefresh ? localNextOffset : '',
filter: selectGiftProfileFilter(global, peerId, tabId), filter: fetchingFilter,
}); });
if (!result) { global = getGlobal();
const currentFilter = selectGiftProfileFilter(global, peerId, tabId);
if (!result || currentFilter !== fetchingFilter) {
return; return;
} }
global = getGlobal();
const newGifts = currentGifts && !shouldRefresh ? currentGifts.gifts.concat(result.gifts) : result.gifts; const newGifts = currentGifts && !shouldRefresh ? currentGifts.gifts.concat(result.gifts) : result.gifts;
const tabState = selectTabState(global, tabId);
if (withTransition) {
global = updateTabState(global, {
savedGifts: {
...tabState.savedGifts,
transitionKey: (tabState?.savedGifts.transitionKey || 0) + 1,
},
}, tabId);
}
global = replacePeerSavedGifts(global, peerId, newGifts, result.nextOffset, tabId); global = replacePeerSavedGifts(global, peerId, newGifts, result.nextOffset, tabId);
setGlobal(global); setGlobal(global);
}); });

View File

@ -2,7 +2,7 @@ import type { ActionReturnType } from '../../types';
import { DEFAULT_GIFT_PROFILE_FILTER_OPTIONS } from '../../../config'; import { DEFAULT_GIFT_PROFILE_FILTER_OPTIONS } from '../../../config';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { addActionHandler } from '../../index'; import { addActionHandler, setGlobal } from '../../index';
import { import {
clearPayment, clearPayment,
updatePayment, updatePayment,
@ -68,7 +68,7 @@ addActionHandler('closeGiftCodeModal', (global, actions, payload): ActionReturnT
}); });
addActionHandler('updateGiftProfileFilter', (global, actions, payload): ActionReturnType => { addActionHandler('updateGiftProfileFilter', (global, actions, payload): ActionReturnType => {
const { filter, tabId = getCurrentTabId() } = payload || {}; const { filter, peerId, tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId); const tabState = selectTabState(global, tabId);
const prevFilter = tabState.savedGifts.filter; const prevFilter = tabState.savedGifts.filter;
@ -98,23 +98,40 @@ addActionHandler('updateGiftProfileFilter', (global, actions, payload): ActionRe
}; };
} }
return updateTabState(global, { global = updateTabState(global, {
savedGifts: { savedGifts: {
giftsByPeerId: {}, ...tabState.savedGifts,
giftsByPeerId: {
[peerId]: tabState.savedGifts.giftsByPeerId[peerId],
},
filter: updatedFilter, filter: updatedFilter,
}, },
}, tabId); }, tabId);
setGlobal(global);
actions.loadPeerSavedGifts({
peerId, shouldRefresh: true, withTransition: true, tabId: tabState.id,
});
}); });
addActionHandler('resetGiftProfileFilter', (global, actions, payload): ActionReturnType => { addActionHandler('resetGiftProfileFilter', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {}; const { peerId, tabId = getCurrentTabId() } = payload || {};
const tabState = selectTabState(global, tabId);
return updateTabState(global, { global = updateTabState(global, {
savedGifts: { savedGifts: {
giftsByPeerId: {}, ...tabState.savedGifts,
giftsByPeerId: {
[peerId]: tabState.savedGifts.giftsByPeerId[peerId],
},
filter: { filter: {
...DEFAULT_GIFT_PROFILE_FILTER_OPTIONS, ...DEFAULT_GIFT_PROFILE_FILTER_OPTIONS,
}, },
}, },
}, tabId); }, tabId);
setGlobal(global);
actions.loadPeerSavedGifts({
peerId, shouldRefresh: true, withTransition: true, tabId: tabState.id,
});
}); });

View File

@ -2369,6 +2369,7 @@ export interface ActionPayloads {
loadPeerSavedGifts: { loadPeerSavedGifts: {
peerId: string; peerId: string;
shouldRefresh?: boolean; shouldRefresh?: boolean;
withTransition?: boolean;
} & WithTabId; } & WithTabId;
changeGiftVisibility: { changeGiftVisibility: {
gift: ApiInputSavedStarGift; gift: ApiInputSavedStarGift;
@ -2397,9 +2398,12 @@ export interface ActionPayloads {
closeSuggestedStatusModal: WithTabId | undefined; closeSuggestedStatusModal: WithTabId | undefined;
updateGiftProfileFilter: { updateGiftProfileFilter: {
peerId: string;
filter: Partial<GiftProfileFilterOptions>; filter: Partial<GiftProfileFilterOptions>;
} & WithTabId; } & WithTabId;
resetGiftProfileFilter: WithTabId | undefined; resetGiftProfileFilter: {
peerId: string;
} & WithTabId;
// Invoice // Invoice
openInvoice: Exclude<ApiInputInvoice, ApiInputInvoiceStarGift> & WithTabId; openInvoice: Exclude<ApiInputInvoice, ApiInputInvoiceStarGift> & WithTabId;

View File

@ -209,6 +209,7 @@ export type TabState = {
savedGifts: { savedGifts: {
giftsByPeerId: Record<string, ApiSavedGifts>; giftsByPeerId: Record<string, ApiSavedGifts>;
filter: GiftProfileFilterOptions; filter: GiftProfileFilterOptions;
transitionKey?: number;
}; };
globalSearch: { globalSearch: {