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

View File

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

View File

@ -138,7 +138,7 @@ addActionHandler('loadStarGifts', async (global): Promise<void> => {
addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise<void> => {
const {
peerId, shouldRefresh, tabId = getCurrentTabId(),
peerId, shouldRefresh, withTransition, tabId = getCurrentTabId(),
} = payload;
const peer = selectPeer(global, peerId);
@ -149,20 +149,35 @@ addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise
if (!shouldRefresh && currentGifts && !localNextOffset) return; // Already loaded all
global = getGlobal();
const fetchingFilter = selectGiftProfileFilter(global, peerId, tabId);
const result = await callApi('fetchSavedStarGifts', {
peer,
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;
}
global = getGlobal();
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);
setGlobal(global);
});

View File

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

View File

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

View File

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