Gifts: Support filter in profile (#5533)
This commit is contained in:
parent
0ac730a043
commit
1419d396d3
@ -1,6 +1,9 @@
|
||||
import BigInt from 'big-integer';
|
||||
import { Api as GramJs } from '../../../lib/gramjs';
|
||||
|
||||
import type {
|
||||
GiftProfileFilterOptions,
|
||||
} from '../../../types';
|
||||
import type {
|
||||
ApiChat,
|
||||
ApiInputStorePaymentPurpose,
|
||||
@ -451,16 +454,30 @@ export async function fetchSavedStarGifts({
|
||||
peer,
|
||||
offset = '',
|
||||
limit,
|
||||
filter,
|
||||
}: {
|
||||
peer: ApiPeer;
|
||||
offset?: string;
|
||||
limit?: number;
|
||||
filter?: GiftProfileFilterOptions;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.payments.GetSavedStarGifts({
|
||||
type GetSavedStarGiftsParams = ConstructorParameters<typeof GramJs.payments.GetSavedStarGifts>[0];
|
||||
|
||||
const params : GetSavedStarGiftsParams = {
|
||||
peer: buildInputPeer(peer.id, peer.accessHash),
|
||||
offset,
|
||||
limit,
|
||||
}));
|
||||
...(filter && {
|
||||
sortByValue: filter.sortType === 'byValue' || undefined,
|
||||
excludeUnlimited: !filter.shouldIncludeUnlimited || undefined,
|
||||
excludeLimited: !filter.shouldIncludeLimited || undefined,
|
||||
excludeUnique: !filter.shouldIncludeUnique || undefined,
|
||||
excludeSaved: !filter.shouldIncludeDisplayed || undefined,
|
||||
excludeUnsaved: !filter.shouldIncludeHidden || undefined,
|
||||
} satisfies GetSavedStarGiftsParams),
|
||||
};
|
||||
|
||||
const result = await invokeRequest(new GramJs.payments.GetSavedStarGifts(params));
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
|
||||
@ -1599,6 +1599,15 @@
|
||||
"ViewButtonGiftUnique" = "VIEW COLLECTIBLE";
|
||||
"AuthContinueOnThisLanguage" = "Continue in English";
|
||||
"Share" = "Share";
|
||||
"GiftSortByDate" = "Sort by Date";
|
||||
"GiftSortByValue" = "Sort by Value";
|
||||
"GiftFilterUnlimited" = "Unlimited";
|
||||
"GiftFilterLimited" = "Limited";
|
||||
"GiftFilterUnique" = "Unique";
|
||||
"GiftFilterDisplayed" = "Displayed";
|
||||
"GiftFilterHidden" = "Hidden";
|
||||
"GiftSearchEmpty" = "No matching gifts";
|
||||
"GiftSearchReset" = "View All Gifts";
|
||||
"CheckPasswordTitle" = "Enter Password";
|
||||
"CheckPasswordPlaceholder" = "Password";
|
||||
"CheckPasswordDescription" = "Please enter your password to continue.";
|
||||
|
||||
BIN
src/assets/tgs/SearchingDuck.tgs
Normal file
BIN
src/assets/tgs/SearchingDuck.tgs
Normal file
Binary file not shown.
@ -20,6 +20,7 @@ import MonkeyPeek from '../../../assets/tgs/monkeys/TwoFactorSetupMonkeyPeek.tgs
|
||||
import MonkeyTracking from '../../../assets/tgs/monkeys/TwoFactorSetupMonkeyTracking.tgs';
|
||||
import ReadTime from '../../../assets/tgs/ReadTime.tgs';
|
||||
import Report from '../../../assets/tgs/Report.tgs';
|
||||
import SearchingDuck from '../../../assets/tgs/SearchingDuck.tgs';
|
||||
import Congratulations from '../../../assets/tgs/settings/Congratulations.tgs';
|
||||
import DiscussionGroups from '../../../assets/tgs/settings/DiscussionGroupsDucks.tgs';
|
||||
import Experimental from '../../../assets/tgs/settings/Experimental.tgs';
|
||||
@ -64,4 +65,5 @@ export const LOCAL_TGS_URLS = {
|
||||
StarReactionEffect,
|
||||
StarReaction,
|
||||
Report,
|
||||
SearchingDuck,
|
||||
};
|
||||
|
||||
@ -46,6 +46,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
.nothing-found-gifts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
padding-top: 5rem;
|
||||
height: 100%;
|
||||
|
||||
.description {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 1rem;
|
||||
font-weight: var(--font-weight-medium);
|
||||
text-align: center;
|
||||
margin-block: 1rem;
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
|
||||
.Link {
|
||||
color: var(--color-links);
|
||||
font-weight: var(--font-weight-medium);
|
||||
transition: opacity 0.15s ease-in;
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shared-media {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
|
||||
@ -49,6 +49,7 @@ import {
|
||||
selectChatMessages,
|
||||
selectCurrentSharedMediaSearch,
|
||||
selectIsCurrentUserPremium,
|
||||
selectIsGiftProfileFilterDefault,
|
||||
selectIsRightColumnShown,
|
||||
selectPeerStories,
|
||||
selectSimilarBotsIds,
|
||||
@ -63,6 +64,7 @@ import { selectPremiumLimit } from '../../global/selectors/limits';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { captureEvents, SwipeDirection } from '../../util/captureEvents';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
import { getSenderName } from '../left/search/helpers/getSenderName';
|
||||
|
||||
@ -79,6 +81,7 @@ import useProfileState from './hooks/useProfileState';
|
||||
import useProfileViewportIds from './hooks/useProfileViewportIds';
|
||||
import useTransitionFixes from './hooks/useTransitionFixes';
|
||||
|
||||
import AnimatedIconWithPreview from '../common/AnimatedIconWithPreview';
|
||||
import Audio from '../common/Audio';
|
||||
import Document from '../common/Document';
|
||||
import SavedGift from '../common/gift/SavedGift';
|
||||
@ -96,6 +99,7 @@ import MediaStory from '../story/MediaStory';
|
||||
import Button from '../ui/Button';
|
||||
import FloatingActionButton from '../ui/FloatingActionButton';
|
||||
import InfiniteScroll from '../ui/InfiniteScroll';
|
||||
import Link from '../ui/Link';
|
||||
import ListItem, { type MenuItemContextAction } from '../ui/ListItem';
|
||||
import Spinner from '../ui/Spinner';
|
||||
import TabList from '../ui/TabList';
|
||||
@ -155,6 +159,7 @@ type StateProps = {
|
||||
isSavedDialog?: boolean;
|
||||
forceScrollProfileTab?: boolean;
|
||||
isSynced?: boolean;
|
||||
isNotDefaultGiftFilter?: boolean;
|
||||
};
|
||||
|
||||
type TabProps = {
|
||||
@ -219,6 +224,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
forceScrollProfileTab,
|
||||
isSynced,
|
||||
onProfileStateChange,
|
||||
isNotDefaultGiftFilter,
|
||||
}) => {
|
||||
const {
|
||||
setSharedMediaSearchType,
|
||||
@ -237,6 +243,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
loadBotRecommendations,
|
||||
loadPreviewMedias,
|
||||
loadPeerSavedGifts,
|
||||
resetGiftProfileFilter,
|
||||
} = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -482,6 +489,10 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
setActiveTab(Math.min(newActiveTab, tabs.length - 1));
|
||||
}, [hasMembersTab, activeTab, tabs]);
|
||||
|
||||
const handleResetGiftsFilter = useLastCallback(() => {
|
||||
resetGiftProfileFilter();
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!transitionRef.current || !IS_TOUCH_ENV) {
|
||||
return undefined;
|
||||
@ -523,6 +534,28 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
}];
|
||||
}
|
||||
|
||||
function renderNothingFoundGiftsWithFilter() {
|
||||
return (
|
||||
<div className="nothing-found-gifts">
|
||||
<AnimatedIconWithPreview
|
||||
size={160}
|
||||
tgsUrl={LOCAL_TGS_URLS.SearchingDuck}
|
||||
nonInteractive
|
||||
noLoop
|
||||
/>
|
||||
<div className="description">
|
||||
{lang('GiftSearchEmpty')}
|
||||
</div>
|
||||
<Link
|
||||
className="date"
|
||||
onClick={handleResetGiftsFilter}
|
||||
>
|
||||
{lang('GiftSearchReset')}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderContent() {
|
||||
if (resultType === 'dialogs') {
|
||||
return (
|
||||
@ -535,7 +568,9 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
const forceRenderHiddenMembers = Boolean(resultType === 'members' && areMembersHidden);
|
||||
|
||||
return (
|
||||
<div className="content empty-list">
|
||||
<div
|
||||
className="content empty-list"
|
||||
>
|
||||
{!noSpinner && !forceRenderHiddenMembers && <Spinner />}
|
||||
{forceRenderHiddenMembers && <NothingFound text="You have no access to group members list." />}
|
||||
</div>
|
||||
@ -545,6 +580,10 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
if (viewportIds && !viewportIds?.length) {
|
||||
let text: string;
|
||||
|
||||
if (resultType === 'gifts' && isNotDefaultGiftFilter) {
|
||||
return renderNothingFoundGiftsWithFilter();
|
||||
}
|
||||
|
||||
switch (resultType) {
|
||||
case 'members':
|
||||
text = areMembersHidden ? 'You have no access to group members list.' : 'No members found';
|
||||
@ -912,7 +951,9 @@ export default memo(withGlobal<OwnProps>(
|
||||
const archiveStoryIds = peerStories?.archiveIds;
|
||||
|
||||
const hasGiftsTab = Boolean(peerFullInfo?.starGiftCount) && !isSavedDialog;
|
||||
const peerGifts = global.peers.giftsById[chatId];
|
||||
const peerGifts = selectTabState(global).savedGifts.giftsByPeerId[chatId];
|
||||
|
||||
const isNotDefaultGiftFilter = !selectIsGiftProfileFilterDefault(global);
|
||||
|
||||
return {
|
||||
theme: selectTheme(global),
|
||||
@ -952,6 +993,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isTopicInfo,
|
||||
isSavedDialog,
|
||||
isSynced: global.isSynced,
|
||||
isNotDefaultGiftFilter,
|
||||
limitSimilarPeers: selectPremiumLimit(global, 'recommendedChannels'),
|
||||
...(hasMembersTab && members && { members, adminMembersById }),
|
||||
...(hasCommonChatsTab && user && { commonChatIds: commonChats?.ids }),
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, { useEffect, useRef, useState } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
useEffect, useMemo, useRef, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiExportedInvite } from '../../api/types';
|
||||
import type { GiftProfileFilterOptions, ThreadId } from '../../types';
|
||||
import { MAIN_THREAD_ID } from '../../api/types';
|
||||
import { ManagementScreens, ProfileState, type ThreadId } from '../../types';
|
||||
import { ManagementScreens, ProfileState } from '../../types';
|
||||
|
||||
import { ANIMATION_END_DELAY, SAVED_FOLDER_ID } from '../../config';
|
||||
import {
|
||||
@ -12,6 +15,8 @@ import {
|
||||
} from '../../global/helpers';
|
||||
import {
|
||||
selectCanManage,
|
||||
selectCanUseGiftProfileAdminFilter,
|
||||
selectCanUseGiftProfileFilter,
|
||||
selectChat,
|
||||
selectChatFullInfo,
|
||||
selectCurrentGifSearch,
|
||||
@ -28,12 +33,16 @@ import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||
import useElectronDrag from '../../hooks/useElectronDrag';
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
import { useFolderManagerForChatsCount } from '../../hooks/useFolderManager';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
import useOldLang from '../../hooks/useOldLang';
|
||||
|
||||
import Icon from '../common/icons/Icon';
|
||||
import Button from '../ui/Button';
|
||||
import ConfirmDialog from '../ui/ConfirmDialog';
|
||||
import DropdownMenu from '../ui/DropdownMenu';
|
||||
import MenuItem from '../ui/MenuItem';
|
||||
import MenuSeparator from '../ui/MenuSeparator';
|
||||
import SearchInput from '../ui/SearchInput';
|
||||
import Transition from '../ui/Transition';
|
||||
|
||||
@ -76,6 +85,9 @@ type StateProps = {
|
||||
shouldSkipHistoryAnimations?: boolean;
|
||||
isBot?: boolean;
|
||||
canEditBot?: boolean;
|
||||
giftProfileFilter: GiftProfileFilterOptions;
|
||||
canUseGiftFilter?: boolean;
|
||||
canUseGiftAdminFilter?:boolean;
|
||||
isInsideTopic?: boolean;
|
||||
canEditTopic?: boolean;
|
||||
isSavedMessages?: boolean;
|
||||
@ -86,6 +98,7 @@ const COLUMN_ANIMATION_DURATION = 450 + ANIMATION_END_DELAY;
|
||||
enum HeaderContent {
|
||||
Profile,
|
||||
MemberList,
|
||||
GiftList,
|
||||
SharedMedia,
|
||||
StoryList,
|
||||
Search,
|
||||
@ -161,6 +174,9 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
onClose,
|
||||
onScreenSelect,
|
||||
canEditBot,
|
||||
giftProfileFilter,
|
||||
canUseGiftFilter,
|
||||
canUseGiftAdminFilter,
|
||||
}) => {
|
||||
const {
|
||||
setStickerSearchQuery,
|
||||
@ -171,11 +187,21 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
setEditingExportedInvite,
|
||||
deleteExportedChatInvite,
|
||||
openEditTopicPanel,
|
||||
updateGiftProfileFilter,
|
||||
} = getActions();
|
||||
|
||||
const [isDeleteDialogOpen, openDeleteDialog, closeDeleteDialog] = useFlag();
|
||||
const { isMobile } = useAppLayout();
|
||||
|
||||
const {
|
||||
sortType: giftsSortType,
|
||||
shouldIncludeUnlimited: shouldIncludeUnlimitedGifts,
|
||||
shouldIncludeLimited: shouldIncludeLimitedGifts,
|
||||
shouldIncludeUnique: shouldIncludeUniqueGifts,
|
||||
shouldIncludeDisplayed: shouldIncludeDisplayedGifts,
|
||||
shouldIncludeHidden: shouldIncludeHiddenGifts,
|
||||
} = giftProfileFilter;
|
||||
|
||||
const foldersChatCount = useFolderManagerForChatsCount();
|
||||
|
||||
const handleEditInviteClick = useLastCallback(() => {
|
||||
@ -226,7 +252,8 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
}, COLUMN_ANIMATION_DURATION);
|
||||
}, [isColumnOpen]);
|
||||
|
||||
const lang = useOldLang();
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
const contentKey = isProfile ? (
|
||||
profileState === ProfileState.Profile ? (
|
||||
HeaderContent.Profile
|
||||
@ -234,6 +261,8 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
HeaderContent.SharedMedia
|
||||
) : profileState === ProfileState.MemberList ? (
|
||||
HeaderContent.MemberList
|
||||
) : profileState === ProfileState.GiftList ? (
|
||||
HeaderContent.GiftList
|
||||
) : profileState === ProfileState.StoryList ? (
|
||||
HeaderContent.StoryList
|
||||
) : profileState === ProfileState.SavedDialogs ? (
|
||||
@ -309,24 +338,40 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
|
||||
function getHeaderTitle() {
|
||||
if (isSavedMessages) {
|
||||
return lang('SavedMessages');
|
||||
return oldLang('SavedMessages');
|
||||
}
|
||||
|
||||
if (isInsideTopic) {
|
||||
return lang('AccDescrTopic');
|
||||
return oldLang('AccDescrTopic');
|
||||
}
|
||||
|
||||
if (isChannel) {
|
||||
return lang('Channel.TitleInfo');
|
||||
return oldLang('Channel.TitleInfo');
|
||||
}
|
||||
|
||||
if (userId) {
|
||||
return lang(isBot ? 'lng_info_bot_title' : 'lng_info_user_title');
|
||||
return oldLang(isBot ? 'lng_info_bot_title' : 'lng_info_user_title');
|
||||
}
|
||||
|
||||
return lang('GroupInfo.Title');
|
||||
return oldLang('GroupInfo.Title');
|
||||
}
|
||||
|
||||
const PrimaryLinkMenuButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => {
|
||||
return ({ onTrigger, isOpen }) => (
|
||||
<Button
|
||||
round
|
||||
ripple={!isMobile}
|
||||
size="smaller"
|
||||
color="translucent"
|
||||
className={isOpen ? 'active' : ''}
|
||||
onClick={onTrigger}
|
||||
ariaLabel={lang('AccDescrOpenMenu2')}
|
||||
>
|
||||
<Icon name="more" />
|
||||
</Button>
|
||||
);
|
||||
}, [isMobile, lang]);
|
||||
|
||||
function renderHeaderContent() {
|
||||
if (renderingContentKey === -1) {
|
||||
return undefined;
|
||||
@ -334,48 +379,48 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
|
||||
switch (renderingContentKey) {
|
||||
case HeaderContent.PollResults:
|
||||
return <h3 className="title">{lang('PollResults')}</h3>;
|
||||
return <h3 className="title">{oldLang('PollResults')}</h3>;
|
||||
case HeaderContent.AddingMembers:
|
||||
return <h3 className="title">{lang(isChannel ? 'ChannelAddSubscribers' : 'GroupAddMembers')}</h3>;
|
||||
return <h3 className="title">{oldLang(isChannel ? 'ChannelAddSubscribers' : 'GroupAddMembers')}</h3>;
|
||||
case HeaderContent.ManageInitial:
|
||||
return <h3 className="title">{lang('Edit')}</h3>;
|
||||
return <h3 className="title">{oldLang('Edit')}</h3>;
|
||||
case HeaderContent.ManageChatPrivacyType:
|
||||
return <h3 className="title">{lang(isChannel ? 'ChannelTypeHeader' : 'GroupTypeHeader')}</h3>;
|
||||
return <h3 className="title">{oldLang(isChannel ? 'ChannelTypeHeader' : 'GroupTypeHeader')}</h3>;
|
||||
case HeaderContent.ManageDiscussion:
|
||||
return <h3 className="title">{lang('Discussion')}</h3>;
|
||||
return <h3 className="title">{oldLang('Discussion')}</h3>;
|
||||
case HeaderContent.ManageChatAdministrators:
|
||||
return <h3 className="title">{lang('ChannelAdministrators')}</h3>;
|
||||
return <h3 className="title">{oldLang('ChannelAdministrators')}</h3>;
|
||||
case HeaderContent.ManageGroupRecentActions:
|
||||
return <h3 className="title">{lang('Group.Info.AdminLog')}</h3>;
|
||||
return <h3 className="title">{oldLang('Group.Info.AdminLog')}</h3>;
|
||||
case HeaderContent.ManageGroupAdminRights:
|
||||
return <h3 className="title">{lang('EditAdminRights')}</h3>;
|
||||
return <h3 className="title">{oldLang('EditAdminRights')}</h3>;
|
||||
case HeaderContent.ManageGroupNewAdminRights:
|
||||
return <h3 className="title">{lang('SetAsAdmin')}</h3>;
|
||||
return <h3 className="title">{oldLang('SetAsAdmin')}</h3>;
|
||||
case HeaderContent.ManageGroupPermissions:
|
||||
return <h3 className="title">{lang('ChannelPermissions')}</h3>;
|
||||
return <h3 className="title">{oldLang('ChannelPermissions')}</h3>;
|
||||
case HeaderContent.ManageGroupRemovedUsers:
|
||||
return <h3 className="title">{lang('BlockedUsers')}</h3>;
|
||||
return <h3 className="title">{oldLang('BlockedUsers')}</h3>;
|
||||
case HeaderContent.ManageChannelRemovedUsers:
|
||||
return <h3 className="title">{lang('ChannelBlockedUsers')}</h3>;
|
||||
return <h3 className="title">{oldLang('ChannelBlockedUsers')}</h3>;
|
||||
case HeaderContent.ManageGroupUserPermissionsCreate:
|
||||
return <h3 className="title">{lang('ChannelAddException')}</h3>;
|
||||
return <h3 className="title">{oldLang('ChannelAddException')}</h3>;
|
||||
case HeaderContent.ManageGroupUserPermissions:
|
||||
return <h3 className="title">{lang('UserRestrictions')}</h3>;
|
||||
return <h3 className="title">{oldLang('UserRestrictions')}</h3>;
|
||||
case HeaderContent.ManageInvites:
|
||||
return <h3 className="title">{lang('lng_group_invite_title')}</h3>;
|
||||
return <h3 className="title">{oldLang('lng_group_invite_title')}</h3>;
|
||||
case HeaderContent.ManageEditInvite:
|
||||
return <h3 className="title">{isEditingInvite ? lang('EditLink') : lang('NewLink')}</h3>;
|
||||
return <h3 className="title">{isEditingInvite ? oldLang('EditLink') : oldLang('NewLink')}</h3>;
|
||||
case HeaderContent.ManageInviteInfo:
|
||||
return (
|
||||
<>
|
||||
<h3 className="title">{lang('InviteLink')}</h3>
|
||||
<h3 className="title">{oldLang('InviteLink')}</h3>
|
||||
<section className="tools">
|
||||
{currentInviteInfo && !currentInviteInfo.isRevoked && (
|
||||
<Button
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Edit')}
|
||||
ariaLabel={oldLang('Edit')}
|
||||
onClick={handleEditInviteClick}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
@ -387,7 +432,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
color="danger"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Delete')}
|
||||
ariaLabel={oldLang('Delete')}
|
||||
onClick={openDeleteDialog}
|
||||
>
|
||||
<Icon name="delete" />
|
||||
@ -395,10 +440,10 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
<ConfirmDialog
|
||||
isOpen={isDeleteDialogOpen}
|
||||
onClose={closeDeleteDialog}
|
||||
title={lang('DeleteLink')}
|
||||
text={lang('DeleteLinkHelp')}
|
||||
title={oldLang('DeleteLink')}
|
||||
text={oldLang('DeleteLinkHelp')}
|
||||
confirmIsDestructive
|
||||
confirmLabel={lang('Delete')}
|
||||
confirmLabel={oldLang('Delete')}
|
||||
confirmHandler={handleDeleteInviteClick}
|
||||
/>
|
||||
</>
|
||||
@ -407,14 +452,14 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
</>
|
||||
);
|
||||
case HeaderContent.ManageJoinRequests:
|
||||
return <h3 className="title">{isChannel ? lang('SubscribeRequests') : lang('MemberRequests')}</h3>;
|
||||
return <h3 className="title">{isChannel ? oldLang('SubscribeRequests') : oldLang('MemberRequests')}</h3>;
|
||||
case HeaderContent.ManageGroupAddAdmins:
|
||||
return <h3 className="title">{lang('Channel.Management.AddModerator')}</h3>;
|
||||
return <h3 className="title">{oldLang('Channel.Management.AddModerator')}</h3>;
|
||||
case HeaderContent.StickerSearch:
|
||||
return (
|
||||
<SearchInput
|
||||
value={stickerSearchQuery}
|
||||
placeholder={lang('SearchStickersHint')}
|
||||
placeholder={oldLang('SearchStickersHint')}
|
||||
autoFocusSearch
|
||||
onChange={handleStickerSearchQueryChange}
|
||||
/>
|
||||
@ -423,43 +468,125 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
return (
|
||||
<SearchInput
|
||||
value={gifSearchQuery}
|
||||
placeholder={lang('SearchGifsTitle')}
|
||||
placeholder={oldLang('SearchGifsTitle')}
|
||||
autoFocusSearch
|
||||
onChange={handleGifSearchQueryChange}
|
||||
/>
|
||||
);
|
||||
case HeaderContent.Statistics:
|
||||
return <h3 className="title">{lang(isChannel ? 'ChannelStats.Title' : 'GroupStats.Title')}</h3>;
|
||||
return <h3 className="title">{oldLang(isChannel ? 'ChannelStats.Title' : 'GroupStats.Title')}</h3>;
|
||||
case HeaderContent.MessageStatistics:
|
||||
return <h3 className="title">{lang('Stats.MessageTitle')}</h3>;
|
||||
return <h3 className="title">{oldLang('Stats.MessageTitle')}</h3>;
|
||||
case HeaderContent.StoryStatistics:
|
||||
return <h3 className="title">{lang('Stats.StoryTitle')}</h3>;
|
||||
return <h3 className="title">{oldLang('Stats.StoryTitle')}</h3>;
|
||||
case HeaderContent.BoostStatistics:
|
||||
return <h3 className="title">{lang('Boosts')}</h3>;
|
||||
return <h3 className="title">{oldLang('Boosts')}</h3>;
|
||||
case HeaderContent.MonetizationStatistics:
|
||||
return <h3 className="title">{lang('lng_channel_earn_title')}</h3>;
|
||||
return <h3 className="title">{oldLang('lng_channel_earn_title')}</h3>;
|
||||
case HeaderContent.SharedMedia:
|
||||
return <h3 className="title">{lang('SharedMedia')}</h3>;
|
||||
return <h3 className="title">{oldLang('SharedMedia')}</h3>;
|
||||
case HeaderContent.ManageChannelSubscribers:
|
||||
return <h3 className="title">{lang('ChannelSubscribers')}</h3>;
|
||||
return <h3 className="title">{oldLang('ChannelSubscribers')}</h3>;
|
||||
case HeaderContent.MemberList:
|
||||
case HeaderContent.ManageGroupMembers:
|
||||
return <h3 className="title">{lang('GroupMembers')}</h3>;
|
||||
return <h3 className="title">{oldLang('GroupMembers')}</h3>;
|
||||
case HeaderContent.StoryList:
|
||||
return <h3 className="title">{lang(isSelf ? 'Settings.MyStories' : 'PeerInfo.PaneStories')}</h3>;
|
||||
return <h3 className="title">{oldLang(isSelf ? 'Settings.MyStories' : 'PeerInfo.PaneStories')}</h3>;
|
||||
case HeaderContent.SavedDialogs:
|
||||
return (
|
||||
<div className="header">
|
||||
<h3 className="title">{lang('SavedMessagesTab')}</h3>
|
||||
<div className="subtitle">{lang('Chats', foldersChatCount[SAVED_FOLDER_ID])}</div>
|
||||
<h3 className="title">{oldLang('SavedMessagesTab')}</h3>
|
||||
<div className="subtitle">{oldLang('Chats', foldersChatCount[SAVED_FOLDER_ID])}</div>
|
||||
</div>
|
||||
);
|
||||
case HeaderContent.ManageReactions:
|
||||
return <h3 className="title">{lang('Reactions')}</h3>;
|
||||
return <h3 className="title">{oldLang('Reactions')}</h3>;
|
||||
case HeaderContent.CreateTopic:
|
||||
return <h3 className="title">{lang('NewTopic')}</h3>;
|
||||
return <h3 className="title">{oldLang('NewTopic')}</h3>;
|
||||
case HeaderContent.EditTopic:
|
||||
return <h3 className="title">{lang('EditTopic')}</h3>;
|
||||
return <h3 className="title">{oldLang('EditTopic')}</h3>;
|
||||
case HeaderContent.GiftList:
|
||||
return (
|
||||
<>
|
||||
<h3 className="title">{lang('ProfileTabGifts')}</h3>
|
||||
{canUseGiftFilter && (
|
||||
<section className="tools">
|
||||
<DropdownMenu
|
||||
trigger={PrimaryLinkMenuButton}
|
||||
positionX="right"
|
||||
autoClose={false}
|
||||
>
|
||||
<MenuItem
|
||||
icon={giftsSortType === 'byDate' ? 'calendar-filter' : 'cash-circle'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => updateGiftProfileFilter(
|
||||
{ filter: { sortType: giftsSortType === 'byDate' ? 'byValue' : 'byDate' } },
|
||||
)}
|
||||
>
|
||||
{lang(giftsSortType === 'byDate' ? 'GiftSortByDate' : 'GiftSortByValue')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuSeparator />
|
||||
|
||||
<MenuItem
|
||||
icon={shouldIncludeUnlimitedGifts ? 'check' : 'placeholder'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => updateGiftProfileFilter(
|
||||
{ filter: { shouldIncludeUnlimited: !shouldIncludeUnlimitedGifts } },
|
||||
)}
|
||||
>
|
||||
{lang('GiftFilterUnlimited')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem
|
||||
icon={shouldIncludeLimitedGifts ? 'check' : 'placeholder'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => updateGiftProfileFilter(
|
||||
{ filter: { shouldIncludeLimited: !shouldIncludeLimitedGifts } },
|
||||
)}
|
||||
>
|
||||
{lang('GiftFilterLimited')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem
|
||||
icon={shouldIncludeUniqueGifts ? 'check' : 'placeholder'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => updateGiftProfileFilter(
|
||||
{ filter: { shouldIncludeUnique: !shouldIncludeUniqueGifts } },
|
||||
)}
|
||||
>
|
||||
{lang('GiftFilterUnique')}
|
||||
</MenuItem>
|
||||
|
||||
{canUseGiftAdminFilter && (
|
||||
<>
|
||||
<MenuSeparator />
|
||||
<MenuItem
|
||||
icon={shouldIncludeDisplayedGifts ? 'check' : 'placeholder'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => updateGiftProfileFilter(
|
||||
{ filter: { shouldIncludeDisplayed: !shouldIncludeDisplayedGifts } },
|
||||
)}
|
||||
>
|
||||
{lang('GiftFilterDisplayed')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem
|
||||
icon={shouldIncludeHiddenGifts ? 'check' : 'placeholder'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => updateGiftProfileFilter(
|
||||
{ filter: { shouldIncludeHidden: !shouldIncludeHiddenGifts } },
|
||||
)}
|
||||
>
|
||||
{lang('GiftFilterHidden')}
|
||||
</MenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</section>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
@ -472,7 +599,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('AddContact')}
|
||||
ariaLabel={oldLang('AddContact')}
|
||||
onClick={handleAddContact}
|
||||
>
|
||||
<Icon name="add-user" />
|
||||
@ -483,7 +610,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Edit')}
|
||||
ariaLabel={oldLang('Edit')}
|
||||
onClick={handleToggleManagement}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
@ -494,7 +621,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Edit')}
|
||||
ariaLabel={oldLang('Edit')}
|
||||
onClick={handleToggleManagement}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
@ -505,7 +632,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('EditTopic')}
|
||||
ariaLabel={oldLang('EditTopic')}
|
||||
onClick={toggleEditTopic}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
@ -516,7 +643,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Statistics')}
|
||||
ariaLabel={oldLang('Statistics')}
|
||||
onClick={handleToggleStatistics}
|
||||
>
|
||||
<Icon name="stats" />
|
||||
@ -531,6 +658,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
const isBackButton = isMobile || (
|
||||
!isSavedMessages && (
|
||||
contentKey === HeaderContent.SharedMedia
|
||||
|| contentKey === HeaderContent.GiftList
|
||||
|| contentKey === HeaderContent.MemberList
|
||||
|| contentKey === HeaderContent.StoryList
|
||||
|| contentKey === HeaderContent.AddingMembers
|
||||
@ -558,7 +686,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
onClick={handleClose}
|
||||
ariaLabel={isBackButton ? lang('Common.Back') : lang('Common.Close')}
|
||||
ariaLabel={isBackButton ? oldLang('Common.Back') : oldLang('Common.Close')}
|
||||
>
|
||||
<div className={buttonClassName} />
|
||||
</Button>
|
||||
@ -599,6 +727,10 @@ export default withGlobal<OwnProps>(
|
||||
const currentInviteInfo = chatId
|
||||
? tabState.management.byChatId[chatId]?.inviteInfo?.invite : undefined;
|
||||
|
||||
const giftProfileFilter = tabState.savedGifts.filter;
|
||||
const canUseGiftFilter = chatId ? selectCanUseGiftProfileFilter(global, chatId) : false;
|
||||
const canUseGiftAdminFilter = chatId ? selectCanUseGiftProfileAdminFilter(global, chatId) : false;
|
||||
|
||||
return {
|
||||
canManage,
|
||||
canAddContact,
|
||||
@ -616,6 +748,9 @@ export default withGlobal<OwnProps>(
|
||||
isSavedMessages,
|
||||
shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations,
|
||||
canEditBot,
|
||||
giftProfileFilter,
|
||||
canUseGiftFilter,
|
||||
canUseGiftAdminFilter,
|
||||
};
|
||||
},
|
||||
)(RightHeader);
|
||||
|
||||
@ -121,6 +121,8 @@ function getStateFromTabType(tabType: ProfileTabType) {
|
||||
switch (tabType) {
|
||||
case 'members':
|
||||
return ProfileState.MemberList;
|
||||
case 'gifts':
|
||||
return ProfileState.GiftList;
|
||||
case 'stories':
|
||||
return ProfileState.StoryList;
|
||||
case 'dialogs':
|
||||
|
||||
@ -24,6 +24,7 @@ type OwnProps = {
|
||||
onTransitionEnd?: NoneToVoidFunction;
|
||||
onMouseEnterBackdrop?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
children: React.ReactNode;
|
||||
autoClose?: boolean;
|
||||
};
|
||||
|
||||
const DropdownMenu: FC<OwnProps> = ({
|
||||
@ -41,6 +42,7 @@ const DropdownMenu: FC<OwnProps> = ({
|
||||
onTransitionEnd,
|
||||
onMouseEnterBackdrop,
|
||||
onHide,
|
||||
autoClose = true,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
@ -110,7 +112,7 @@ const DropdownMenu: FC<OwnProps> = ({
|
||||
positionX={positionX}
|
||||
positionY={positionY}
|
||||
footer={footer}
|
||||
autoClose
|
||||
autoClose={autoClose}
|
||||
onClose={handleClose}
|
||||
onCloseAnimationEnd={onHide}
|
||||
onMouseEnterBackdrop={onMouseEnterBackdrop}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import type {
|
||||
ApiLimitType, ApiLimitTypeForPromo, ApiPremiumSection, ApiReactionEmoji,
|
||||
} from './api/types';
|
||||
import type {
|
||||
GiftProfileFilterOptions,
|
||||
} from './types';
|
||||
|
||||
export const APP_CODE_NAME = 'A';
|
||||
export const APP_NAME = process.env.APP_NAME || `Telegram Web ${APP_CODE_NAME}`;
|
||||
@ -427,3 +430,12 @@ export const PREMIUM_LIMITS_ORDER: ApiLimitTypeForPromo[] = [
|
||||
'dialogFiltersChats',
|
||||
'recommendedChannels',
|
||||
];
|
||||
|
||||
export const DEFAULT_GIFT_PROFILE_FILTER_OPTIONS : GiftProfileFilterOptions = {
|
||||
sortType: 'byDate',
|
||||
shouldIncludeUnlimited: true,
|
||||
shouldIncludeLimited: true,
|
||||
shouldIncludeUnique: true,
|
||||
shouldIncludeDisplayed: true,
|
||||
shouldIncludeHidden: true,
|
||||
} as const;
|
||||
|
||||
@ -15,7 +15,10 @@ import {
|
||||
} from '../../reducers';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import {
|
||||
selectGiftProfileFilter,
|
||||
selectPeer,
|
||||
selectPeerSavedGifts,
|
||||
selectTabState,
|
||||
} from '../../selectors';
|
||||
|
||||
addActionHandler('loadStarStatus', async (global): Promise<void> => {
|
||||
@ -134,12 +137,14 @@ addActionHandler('loadStarGifts', async (global): Promise<void> => {
|
||||
});
|
||||
|
||||
addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise<void> => {
|
||||
const { peerId, shouldRefresh } = payload;
|
||||
const {
|
||||
peerId, shouldRefresh, tabId = getCurrentTabId(),
|
||||
} = payload;
|
||||
|
||||
const peer = selectPeer(global, peerId);
|
||||
if (!peer) return;
|
||||
|
||||
const currentGifts = global.peers.giftsById[peerId];
|
||||
const currentGifts = selectPeerSavedGifts(global, peerId, tabId);
|
||||
const localNextOffset = currentGifts?.nextOffset;
|
||||
|
||||
if (!shouldRefresh && currentGifts && !localNextOffset) return; // Already loaded all
|
||||
@ -147,6 +152,7 @@ addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise
|
||||
const result = await callApi('fetchSavedStarGifts', {
|
||||
peer,
|
||||
offset: !shouldRefresh ? localNextOffset : '',
|
||||
filter: selectGiftProfileFilter(global, peerId, tabId),
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
@ -157,7 +163,7 @@ addActionHandler('loadPeerSavedGifts', async (global, actions, payload): Promise
|
||||
|
||||
const newGifts = currentGifts && !shouldRefresh ? currentGifts.gifts.concat(result.gifts) : result.gifts;
|
||||
|
||||
global = replacePeerSavedGifts(global, peerId, newGifts, result.nextOffset);
|
||||
global = replacePeerSavedGifts(global, peerId, newGifts, result.nextOffset, tabId);
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
@ -216,14 +222,14 @@ addActionHandler('fulfillStarsSubscription', async (global, actions, payload): P
|
||||
});
|
||||
|
||||
addActionHandler('changeGiftVisibility', async (global, actions, payload): Promise<void> => {
|
||||
const { gift, shouldUnsave } = payload;
|
||||
const { gift, shouldUnsave, tabId = getCurrentTabId() } = payload;
|
||||
|
||||
const peerId = gift.type === 'user' ? global.currentUserId! : gift.chatId;
|
||||
|
||||
const requestInputGift = getRequestInputSavedStarGift(global, gift);
|
||||
if (!requestInputGift) return;
|
||||
|
||||
const oldGifts = global.peers.giftsById[peerId];
|
||||
const oldGifts = selectTabState(global, tabId).savedGifts.giftsByPeerId[peerId];
|
||||
if (oldGifts?.gifts?.length) {
|
||||
const newGifts = oldGifts.gifts.map((g) => {
|
||||
if (g.inputGift && areInputSavedGiftsEqual(g.inputGift, gift)) {
|
||||
@ -234,7 +240,7 @@ addActionHandler('changeGiftVisibility', async (global, actions, payload): Promi
|
||||
}
|
||||
return g;
|
||||
});
|
||||
global = replacePeerSavedGifts(global, peerId, newGifts, oldGifts.nextOffset);
|
||||
global = replacePeerSavedGifts(global, peerId, newGifts, oldGifts.nextOffset, tabId);
|
||||
setGlobal(global);
|
||||
}
|
||||
|
||||
@ -245,13 +251,17 @@ addActionHandler('changeGiftVisibility', async (global, actions, payload): Promi
|
||||
global = getGlobal();
|
||||
|
||||
if (!result) {
|
||||
global = replacePeerSavedGifts(global, peerId, oldGifts.gifts, oldGifts.nextOffset);
|
||||
global = replacePeerSavedGifts(global, peerId, oldGifts.gifts, oldGifts.nextOffset, tabId);
|
||||
setGlobal(global);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reload gift list to avoid issues with pagination
|
||||
actions.loadPeerSavedGifts({ peerId, shouldRefresh: true });
|
||||
Object.values(global.byTabId).forEach((tabState) => {
|
||||
if (selectPeerSavedGifts(global, peerId, tabId)) {
|
||||
actions.loadPeerSavedGifts({ peerId, shouldRefresh: true, tabId: tabState.id });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('convertGiftToStars', async (global, actions, payload): Promise<void> => {
|
||||
@ -268,7 +278,12 @@ addActionHandler('convertGiftToStars', async (global, actions, payload): Promise
|
||||
return;
|
||||
}
|
||||
|
||||
actions.loadPeerSavedGifts({ peerId: global.currentUserId!, shouldRefresh: true });
|
||||
const peerId = gift.type === 'user' ? global.currentUserId! : gift.chatId;
|
||||
Object.values(global.byTabId).forEach((tabState) => {
|
||||
if (selectPeerSavedGifts(global, peerId, tabId)) {
|
||||
actions.loadPeerSavedGifts({ peerId, shouldRefresh: true, tabId: tabState.id });
|
||||
}
|
||||
});
|
||||
actions.openStarsBalanceModal({ tabId });
|
||||
});
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import type { ActionReturnType } from '../../types';
|
||||
|
||||
import { DEFAULT_GIFT_PROFILE_FILTER_OPTIONS } from '../../../config';
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
import { addActionHandler } from '../../index';
|
||||
import {
|
||||
@ -65,3 +66,55 @@ addActionHandler('closeGiftCodeModal', (global, actions, payload): ActionReturnT
|
||||
giftCodeModal: undefined,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('updateGiftProfileFilter', (global, actions, payload): ActionReturnType => {
|
||||
const { filter, tabId = getCurrentTabId() } = payload || {};
|
||||
const tabState = selectTabState(global, tabId);
|
||||
|
||||
const prevFilter = tabState.savedGifts.filter;
|
||||
let updatedFilter = {
|
||||
...prevFilter,
|
||||
...filter,
|
||||
};
|
||||
|
||||
if (!updatedFilter.shouldIncludeUnlimited
|
||||
&& !updatedFilter.shouldIncludeLimited
|
||||
&& !updatedFilter.shouldIncludeUnique) {
|
||||
updatedFilter = {
|
||||
...prevFilter,
|
||||
shouldIncludeUnlimited: true,
|
||||
shouldIncludeLimited: true,
|
||||
shouldIncludeUnique: true,
|
||||
...filter,
|
||||
};
|
||||
}
|
||||
|
||||
if (!updatedFilter.shouldIncludeDisplayed && !updatedFilter.shouldIncludeHidden) {
|
||||
updatedFilter = {
|
||||
...prevFilter,
|
||||
shouldIncludeDisplayed: true,
|
||||
shouldIncludeHidden: true,
|
||||
...filter,
|
||||
};
|
||||
}
|
||||
|
||||
return updateTabState(global, {
|
||||
savedGifts: {
|
||||
giftsByPeerId: {},
|
||||
filter: updatedFilter,
|
||||
},
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('resetGiftProfileFilter', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
|
||||
return updateTabState(global, {
|
||||
savedGifts: {
|
||||
giftsByPeerId: {},
|
||||
filter: {
|
||||
...DEFAULT_GIFT_PROFILE_FILTER_OPTIONS,
|
||||
},
|
||||
},
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
@ -5,6 +5,7 @@ import { NewChatMembersProgress } from '../types';
|
||||
import {
|
||||
ANIMATION_LEVEL_DEFAULT,
|
||||
DARK_THEME_PATTERN_COLOR,
|
||||
DEFAULT_GIFT_PROFILE_FILTER_OPTIONS,
|
||||
DEFAULT_MESSAGE_TEXT_SIZE_PX,
|
||||
DEFAULT_PATTERN_COLOR,
|
||||
DEFAULT_PLAYBACK_RATE,
|
||||
@ -106,7 +107,6 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
|
||||
},
|
||||
|
||||
peers: {
|
||||
giftsById: {},
|
||||
profilePhotosById: {},
|
||||
},
|
||||
|
||||
@ -365,6 +365,13 @@ export const INITIAL_TAB_STATE: TabState = {
|
||||
byChatId: {},
|
||||
},
|
||||
|
||||
savedGifts: {
|
||||
filter: {
|
||||
...DEFAULT_GIFT_PROFILE_FILTER_OPTIONS,
|
||||
},
|
||||
giftsByPeerId: {},
|
||||
},
|
||||
|
||||
storyViewer: {
|
||||
isMuted: true,
|
||||
isRibbonShown: false,
|
||||
|
||||
@ -329,20 +329,20 @@ export function replacePeerSavedGifts<T extends GlobalState>(
|
||||
peerId: string,
|
||||
gifts: ApiSavedStarGift[],
|
||||
nextOffset?: string,
|
||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||
): T {
|
||||
global = {
|
||||
...global,
|
||||
peers: {
|
||||
...global.peers,
|
||||
giftsById: {
|
||||
...global.peers.giftsById,
|
||||
const tabState = selectTabState(global, tabId);
|
||||
|
||||
return updateTabState(global, {
|
||||
savedGifts: {
|
||||
...tabState.savedGifts,
|
||||
giftsByPeerId: {
|
||||
...tabState.savedGifts.giftsByPeerId,
|
||||
[peerId]: {
|
||||
gifts,
|
||||
nextOffset,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return global;
|
||||
}, tabId);
|
||||
}
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
import type { GlobalState, TabArgs } from '../types';
|
||||
|
||||
import { DEFAULT_GIFT_PROFILE_FILTER_OPTIONS } from '../../config';
|
||||
import arePropsShallowEqual from '../../util/arePropsShallowEqual';
|
||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||
import {
|
||||
getHasAdminRight, isChatAdmin, isChatChannel,
|
||||
} from '../helpers';
|
||||
import { selectChat } from './chats';
|
||||
import { selectTabState } from './tabs';
|
||||
|
||||
export function selectPaymentInputInvoice<T extends GlobalState>(
|
||||
@ -58,3 +64,32 @@ export function selectSmartGlocalCredentials<T extends GlobalState>(
|
||||
) {
|
||||
return selectTabState(global, tabId).payment.smartGlocalCredentials;
|
||||
}
|
||||
|
||||
export function selectCanUseGiftProfileAdminFilter<T extends GlobalState>(
|
||||
global: T, peerId: string,
|
||||
) {
|
||||
const chat = selectChat(global, peerId);
|
||||
return chat && isChatChannel(chat) && isChatAdmin(chat) && getHasAdminRight(chat, 'postMessages');
|
||||
}
|
||||
|
||||
export function selectCanUseGiftProfileFilter<T extends GlobalState>(
|
||||
global: T, peerId: string,
|
||||
) {
|
||||
const chat = selectChat(global, peerId);
|
||||
return chat && isChatChannel(chat);
|
||||
}
|
||||
|
||||
export function selectGiftProfileFilter<T extends GlobalState>(
|
||||
global: T,
|
||||
peerId: string,
|
||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||
) {
|
||||
return selectCanUseGiftProfileFilter(global, peerId) ? selectTabState(global, tabId).savedGifts.filter : undefined;
|
||||
}
|
||||
|
||||
export function selectIsGiftProfileFilterDefault<T extends GlobalState>(
|
||||
global: T,
|
||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||
) {
|
||||
return arePropsShallowEqual(selectTabState(global, tabId).savedGifts.filter, DEFAULT_GIFT_PROFILE_FILTER_OPTIONS);
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import type { ApiPeer } from '../../api/types';
|
||||
import type { GlobalState } from '../types';
|
||||
import type { ApiPeer, ApiSavedGifts } from '../../api/types';
|
||||
import type { GlobalState, TabArgs } from '../types';
|
||||
|
||||
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../config';
|
||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||
import { selectChat, selectChatFullInfo } from './chats';
|
||||
import { selectTabState } from './tabs';
|
||||
import { selectBot, selectIsPremiumPurchaseBlocked, selectUser } from './users';
|
||||
|
||||
export function selectPeer<T extends GlobalState>(global: T, peerId: string): ApiPeer | undefined {
|
||||
@ -22,3 +24,11 @@ export function selectCanGift<T extends GlobalState>(global: T, peerId: string)
|
||||
return Boolean(!selectIsPremiumPurchaseBlocked(global) && !bot && peerId !== SERVICE_NOTIFICATIONS_USER_ID
|
||||
&& areStarGiftsAvailable);
|
||||
}
|
||||
|
||||
export function selectPeerSavedGifts<T extends GlobalState>(
|
||||
global: T,
|
||||
peerId: string,
|
||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||
) : ApiSavedGifts {
|
||||
return selectTabState(global, tabId).savedGifts.giftsByPeerId[peerId];
|
||||
}
|
||||
|
||||
@ -59,6 +59,7 @@ import type {
|
||||
CallSound,
|
||||
ChatListType,
|
||||
ConfettiParams,
|
||||
GiftProfileFilterOptions,
|
||||
GlobalSearchContent,
|
||||
IAlbum,
|
||||
IAnchorPosition,
|
||||
@ -2341,11 +2342,11 @@ export interface ActionPayloads {
|
||||
loadPeerSavedGifts: {
|
||||
peerId: string;
|
||||
shouldRefresh?: boolean;
|
||||
};
|
||||
} & WithTabId;
|
||||
changeGiftVisibility: {
|
||||
gift: ApiInputSavedStarGift;
|
||||
shouldUnsave?: boolean;
|
||||
};
|
||||
} & WithTabId;
|
||||
convertGiftToStars: {
|
||||
gift: ApiInputSavedStarGift;
|
||||
} & WithTabId;
|
||||
@ -2369,6 +2370,11 @@ export interface ActionPayloads {
|
||||
} & WithTabId;
|
||||
closeSuggestedStatusModal: WithTabId | undefined;
|
||||
|
||||
updateGiftProfileFilter: {
|
||||
filter: Partial<GiftProfileFilterOptions>;
|
||||
} & WithTabId;
|
||||
resetGiftProfileFilter: WithTabId | undefined;
|
||||
|
||||
// Invoice
|
||||
openInvoice: Exclude<ApiInputInvoice, ApiInputInvoiceStarGift> & WithTabId;
|
||||
|
||||
|
||||
@ -24,7 +24,6 @@ import type {
|
||||
ApiQuickReply,
|
||||
ApiReaction,
|
||||
ApiReactionKey,
|
||||
ApiSavedGifts,
|
||||
ApiSavedReactionTag,
|
||||
ApiSession,
|
||||
ApiSponsoredMessage,
|
||||
@ -176,7 +175,6 @@ export type GlobalState = {
|
||||
|
||||
peers: {
|
||||
profilePhotosById: Record<string, ApiPeerPhotos>;
|
||||
giftsById: Record<string, ApiSavedGifts>;
|
||||
};
|
||||
|
||||
chats: {
|
||||
|
||||
@ -32,6 +32,7 @@ import type {
|
||||
ApiPremiumSection,
|
||||
ApiReactionWithPaid,
|
||||
ApiReceiptRegular,
|
||||
ApiSavedGifts,
|
||||
ApiSavedStarGift,
|
||||
ApiStarGift,
|
||||
ApiStarGiftAttribute,
|
||||
@ -57,6 +58,7 @@ import type {
|
||||
ChatRequestedTranslations,
|
||||
ConfettiStyle,
|
||||
FocusDirection,
|
||||
GiftProfileFilterOptions,
|
||||
GlobalSearchContent,
|
||||
IAlbum,
|
||||
IAnchorPosition,
|
||||
@ -202,6 +204,11 @@ export type TabState = {
|
||||
byUsername: Record<string, false | InlineBotSettings>;
|
||||
};
|
||||
|
||||
savedGifts: {
|
||||
giftsByPeerId: Record<string, ApiSavedGifts>;
|
||||
filter: GiftProfileFilterOptions;
|
||||
};
|
||||
|
||||
globalSearch: {
|
||||
query?: string;
|
||||
minDate?: number;
|
||||
|
||||
@ -417,6 +417,7 @@ export enum ProfileState {
|
||||
Profile,
|
||||
SharedMedia,
|
||||
MemberList,
|
||||
GiftList,
|
||||
StoryList,
|
||||
SavedDialogs,
|
||||
}
|
||||
@ -657,3 +658,12 @@ export type CallSound = (
|
||||
export type BotAppPermissions = {
|
||||
geolocation?: boolean;
|
||||
};
|
||||
|
||||
export type GiftProfileFilterOptions = {
|
||||
sortType: 'byDate' | 'byValue';
|
||||
shouldIncludeUnlimited: boolean;
|
||||
shouldIncludeLimited: boolean;
|
||||
shouldIncludeUnique: boolean;
|
||||
shouldIncludeDisplayed: boolean;
|
||||
shouldIncludeHidden: boolean;
|
||||
};
|
||||
|
||||
9
src/types/language.d.ts
vendored
9
src/types/language.d.ts
vendored
@ -1306,6 +1306,15 @@ export interface LangPair {
|
||||
'ViewButtonGiftUnique': undefined;
|
||||
'AuthContinueOnThisLanguage': undefined;
|
||||
'Share': undefined;
|
||||
'GiftSortByDate': undefined;
|
||||
'GiftSortByValue': undefined;
|
||||
'GiftFilterUnlimited': undefined;
|
||||
'GiftFilterLimited': undefined;
|
||||
'GiftFilterUnique': undefined;
|
||||
'GiftFilterDisplayed': undefined;
|
||||
'GiftFilterHidden': undefined;
|
||||
'GiftSearchEmpty': undefined;
|
||||
'GiftSearchReset': undefined;
|
||||
'CheckPasswordTitle': undefined;
|
||||
'CheckPasswordPlaceholder': undefined;
|
||||
'CheckPasswordDescription': undefined;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user