Global Search: Show sponsored results (#5844)
Co-authored-by: Dmitry Kabanov <dmitrykabanovdev@gmail.com> Co-authored-by: Dmitry Kabanov <153344039+dmitrykabanovdev@users.noreply.github.com>
This commit is contained in:
parent
3087fb85cc
commit
97bd26df00
@ -18,6 +18,7 @@ import type {
|
|||||||
ApiRestrictionReason,
|
ApiRestrictionReason,
|
||||||
ApiSendAsPeerId,
|
ApiSendAsPeerId,
|
||||||
ApiSponsoredMessageReportResult,
|
ApiSponsoredMessageReportResult,
|
||||||
|
ApiSponsoredPeer,
|
||||||
ApiStarsSubscriptionPricing,
|
ApiStarsSubscriptionPricing,
|
||||||
ApiTopic,
|
ApiTopic,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
@ -711,3 +712,16 @@ export function buildApiStarsSubscriptionPricing(
|
|||||||
amount: pricing.amount.toJSNumber(),
|
amount: pricing.amount.toJSNumber(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildApiSponsoredPeer(sponsoredPeer: GramJs.SponsoredPeer): ApiSponsoredPeer {
|
||||||
|
const {
|
||||||
|
peer, randomId, additionalInfo, sponsorInfo,
|
||||||
|
} = sponsoredPeer;
|
||||||
|
|
||||||
|
return {
|
||||||
|
peerId: getApiChatIdFromMtpPeer(peer),
|
||||||
|
randomId: serializeBytes(randomId),
|
||||||
|
additionalInfo,
|
||||||
|
sponsorInfo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import {
|
|||||||
buildApiChatlistInvite,
|
buildApiChatlistInvite,
|
||||||
buildApiChatReactions,
|
buildApiChatReactions,
|
||||||
buildApiMissingInvitedUser,
|
buildApiMissingInvitedUser,
|
||||||
|
buildApiSponsoredPeer,
|
||||||
buildApiTopic,
|
buildApiTopic,
|
||||||
buildChatMember,
|
buildChatMember,
|
||||||
buildChatMembers,
|
buildChatMembers,
|
||||||
@ -2020,3 +2021,9 @@ export async function fetchChannelRecommendations({ chat }: { chat?: ApiChat })
|
|||||||
count: result instanceof GramJs.messages.ChatsSlice ? result.count : similarChannels.length,
|
count: result instanceof GramJs.messages.ChatsSlice ? result.count : similarChannels.length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchSponsoredPeer({ query }: { query: string }) {
|
||||||
|
const result = await invokeRequest(new GramJs.contacts.GetSponsoredPeers({ q: query }));
|
||||||
|
if (!result || result instanceof GramJs.contacts.SponsoredPeersEmpty) return undefined;
|
||||||
|
return buildApiSponsoredPeer(result.peers[0]);
|
||||||
|
}
|
||||||
|
|||||||
@ -313,3 +313,10 @@ export type ApiDraft = {
|
|||||||
effectId?: string;
|
effectId?: string;
|
||||||
isLocal?: boolean;
|
isLocal?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiSponsoredPeer = {
|
||||||
|
randomId: string;
|
||||||
|
peerId: string;
|
||||||
|
sponsorInfo?: string;
|
||||||
|
additionalInfo?: string;
|
||||||
|
};
|
||||||
|
|||||||
@ -782,6 +782,7 @@
|
|||||||
"MessageRecommendedLabel" = "recommended";
|
"MessageRecommendedLabel" = "recommended";
|
||||||
"SponsoredMessageAd" = "Ad";
|
"SponsoredMessageAd" = "Ad";
|
||||||
"SponsoredMessageAdWhatIsThis" = "what's this?";
|
"SponsoredMessageAdWhatIsThis" = "what's this?";
|
||||||
|
"SponsoredPeerBadge" = "Ad";
|
||||||
"PremiumStickerTooltip" = "This set contains premium stickers like this one.";
|
"PremiumStickerTooltip" = "This set contains premium stickers like this one.";
|
||||||
"ViewAction" = "View";
|
"ViewAction" = "View";
|
||||||
"Loading" = "Loading...";
|
"Loading" = "Loading...";
|
||||||
|
|||||||
@ -58,8 +58,7 @@ export { default as ChatFolderModal } from '../components/left/ChatFolderModal';
|
|||||||
export { default as MuteChatModal } from '../components/left/MuteChatModal';
|
export { default as MuteChatModal } from '../components/left/MuteChatModal';
|
||||||
|
|
||||||
export { default as ContextMenuContainer } from '../components/middle/message/ContextMenuContainer';
|
export { default as ContextMenuContainer } from '../components/middle/message/ContextMenuContainer';
|
||||||
export { default as SponsoredMessageContextMenuContainer }
|
export { default as SponsoredContextMenuContainer } from '../components/middle/message/SponsoredContextMenuContainer';
|
||||||
from '../components/middle/message/SponsoredMessageContextMenuContainer';
|
|
||||||
export { default as StickerSetModal } from '../components/common/StickerSetModal';
|
export { default as StickerSetModal } from '../components/common/StickerSetModal';
|
||||||
export { default as CustomEmojiSetsModal } from '../components/common/CustomEmojiSetsModal';
|
export { default as CustomEmojiSetsModal } from '../components/common/CustomEmojiSetsModal';
|
||||||
export { default as HeaderMenuContainer } from '../components/middle/HeaderMenuContainer';
|
export { default as HeaderMenuContainer } from '../components/middle/HeaderMenuContainer';
|
||||||
|
|||||||
@ -8,15 +8,21 @@ type OwnProps = {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||||
|
onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BadgeButton = ({
|
const BadgeButton = ({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
|
onMouseDown,
|
||||||
}: OwnProps) => {
|
}: OwnProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={buildClassName(styles.root, onClick && styles.clickable, className)} onClick={onClick}>
|
<div
|
||||||
|
className={buildClassName(styles.root, onClick && styles.clickable, className)}
|
||||||
|
onClick={onClick}
|
||||||
|
onMouseDown={onMouseDown}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import React, {
|
|||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiMessage, ApiMessageSearchContext } from '../../../api/types';
|
import type { ApiMessage, ApiMessageSearchContext, ApiSponsoredPeer } from '../../../api/types';
|
||||||
import { LoadMoreDirection } from '../../../types';
|
import { LoadMoreDirection } from '../../../types';
|
||||||
|
|
||||||
import { ALL_FOLDER_ID, GLOBAL_SUGGESTED_CHANNELS_ID } from '../../../config';
|
import { ALL_FOLDER_ID, GLOBAL_SUGGESTED_CHANNELS_ID } from '../../../config';
|
||||||
@ -27,6 +27,7 @@ import useAppLayout from '../../../hooks/useAppLayout';
|
|||||||
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
|
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
|
||||||
import useEffectOnce from '../../../hooks/useEffectOnce';
|
import useEffectOnce from '../../../hooks/useEffectOnce';
|
||||||
import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
|
import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
|
||||||
|
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
@ -43,6 +44,7 @@ import Transition from '../../ui/Transition';
|
|||||||
import ChatMessage from './ChatMessage';
|
import ChatMessage from './ChatMessage';
|
||||||
import DateSuggest from './DateSuggest';
|
import DateSuggest from './DateSuggest';
|
||||||
import LeftSearchResultChat from './LeftSearchResultChat';
|
import LeftSearchResultChat from './LeftSearchResultChat';
|
||||||
|
import LeftSearchResultSponsored from './LeftSearchResultSponsored';
|
||||||
import RecentContacts from './RecentContacts';
|
import RecentContacts from './RecentContacts';
|
||||||
|
|
||||||
import './ChatResults.scss';
|
import './ChatResults.scss';
|
||||||
@ -62,6 +64,7 @@ type StateProps = {
|
|||||||
accountPeerIds?: string[];
|
accountPeerIds?: string[];
|
||||||
globalPeerIds?: string[];
|
globalPeerIds?: string[];
|
||||||
foundIds?: SearchResultKey[];
|
foundIds?: SearchResultKey[];
|
||||||
|
sponsoredPeer?: ApiSponsoredPeer;
|
||||||
globalMessagesByChatId?: Record<string, { byId: Record<number, ApiMessage> }>;
|
globalMessagesByChatId?: Record<string, { byId: Record<number, ApiMessage> }>;
|
||||||
fetchingStatus?: { chats?: boolean; messages?: boolean };
|
fetchingStatus?: { chats?: boolean; messages?: boolean };
|
||||||
suggestedChannelIds?: string[];
|
suggestedChannelIds?: string[];
|
||||||
@ -69,6 +72,7 @@ type StateProps = {
|
|||||||
|
|
||||||
const MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH = 4;
|
const MIN_QUERY_LENGTH_FOR_GLOBAL_SEARCH = 4;
|
||||||
const LESS_LIST_ITEMS_AMOUNT = 5;
|
const LESS_LIST_ITEMS_AMOUNT = 5;
|
||||||
|
const INTERSECTION_THROTTLE = 200;
|
||||||
|
|
||||||
const runThrottled = throttle((cb) => cb(), 500, false);
|
const runThrottled = throttle((cb) => cb(), 500, false);
|
||||||
|
|
||||||
@ -85,6 +89,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
globalMessagesByChatId,
|
globalMessagesByChatId,
|
||||||
fetchingStatus,
|
fetchingStatus,
|
||||||
suggestedChannelIds,
|
suggestedChannelIds,
|
||||||
|
sponsoredPeer,
|
||||||
onReset,
|
onReset,
|
||||||
onSearchDateSelect,
|
onSearchDateSelect,
|
||||||
}) => {
|
}) => {
|
||||||
@ -93,6 +98,8 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
setGlobalSearchChatId, loadChannelRecommendations,
|
setGlobalSearchChatId, loadChannelRecommendations,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const chatSelectionRef = useRef<HTMLDivElement>(null);
|
const chatSelectionRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -335,7 +342,15 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
&& !localResults.length && !globalResults.length && !actualFoundIds.length;
|
&& !localResults.length && !globalResults.length && !actualFoundIds.length;
|
||||||
const isMessagesFetching = fetchingStatus?.messages;
|
const isMessagesFetching = fetchingStatus?.messages;
|
||||||
|
|
||||||
if (!searchQuery && !searchDate && !isChannelList) {
|
const shouldRenderTopPeers = !searchQuery && !searchDate && !isChannelList;
|
||||||
|
|
||||||
|
const { observe } = useIntersectionObserver({
|
||||||
|
rootRef: containerRef,
|
||||||
|
throttleMs: INTERSECTION_THROTTLE,
|
||||||
|
isDisabled: !shouldRenderTopPeers,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldRenderTopPeers) {
|
||||||
return <RecentContacts onReset={onReset} />;
|
return <RecentContacts onReset={onReset} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,6 +358,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
|
ref={containerRef}
|
||||||
className="LeftSearch--content custom-scroll"
|
className="LeftSearch--content custom-scroll"
|
||||||
items={actualFoundIds}
|
items={actualFoundIds}
|
||||||
onLoadMore={handleLoadMore}
|
onLoadMore={handleLoadMore}
|
||||||
@ -415,6 +431,9 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
{oldLang('DialogList.SearchSectionGlobal')}
|
{oldLang('DialogList.SearchSectionGlobal')}
|
||||||
</h3>
|
</h3>
|
||||||
|
{sponsoredPeer && (
|
||||||
|
<LeftSearchResultSponsored sponsoredPeer={sponsoredPeer} observeIntersection={observe} />
|
||||||
|
)}
|
||||||
{globalResults.map((id, index) => {
|
{globalResults.map((id, index) => {
|
||||||
if (!shouldShowMoreGlobal && index >= LESS_LIST_ITEMS_AMOUNT) {
|
if (!shouldShowMoreGlobal && index >= LESS_LIST_ITEMS_AMOUNT) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -493,7 +512,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
fetchingStatus, globalResults, localResults, resultsByType,
|
fetchingStatus, globalResults, localResults, resultsByType, sponsoredPeer,
|
||||||
} = selectTabState(global).globalSearch;
|
} = selectTabState(global).globalSearch;
|
||||||
const { peerIds: globalPeerIds } = globalResults || {};
|
const { peerIds: globalPeerIds } = globalResults || {};
|
||||||
const { peerIds: accountPeerIds } = localResults || {};
|
const { peerIds: accountPeerIds } = localResults || {};
|
||||||
@ -509,6 +528,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
foundIds,
|
foundIds,
|
||||||
globalMessagesByChatId,
|
globalMessagesByChatId,
|
||||||
fetchingStatus,
|
fetchingStatus,
|
||||||
|
sponsoredPeer,
|
||||||
suggestedChannelIds: similarChannelIds,
|
suggestedChannelIds: similarChannelIds,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -183,6 +183,11 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-sponsored-badge {
|
||||||
|
display: flex;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-section {
|
.search-section {
|
||||||
|
|||||||
116
src/components/left/search/LeftSearchResultSponsored.tsx
Normal file
116
src/components/left/search/LeftSearchResultSponsored.tsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
import React, { memo, useRef } from '../../../lib/teact/teact';
|
||||||
|
import { getActions } from '../../../global';
|
||||||
|
|
||||||
|
import type { ApiSponsoredPeer } from '../../../api/types';
|
||||||
|
import { StoryViewerOrigin } from '../../../types';
|
||||||
|
|
||||||
|
import { isUserId } from '../../../global/helpers';
|
||||||
|
|
||||||
|
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
|
||||||
|
import { useFastClick } from '../../../hooks/useFastClick';
|
||||||
|
import { type ObserveFn, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
import useSelectWithEnter from '../../../hooks/useSelectWithEnter';
|
||||||
|
|
||||||
|
import BadgeButton from '../../common/BadgeButton';
|
||||||
|
import GroupChatInfo from '../../common/GroupChatInfo';
|
||||||
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
|
import SponsoredMessageContextMenuContainer from '../../middle/message/SponsoredContextMenuContainer';
|
||||||
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
sponsoredPeer: ApiSponsoredPeer;
|
||||||
|
observeIntersection?: ObserveFn;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeftSearchResultSponsored: FC<OwnProps> = ({
|
||||||
|
sponsoredPeer,
|
||||||
|
observeIntersection,
|
||||||
|
}) => {
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const { clickSponsored, viewSponsored, openChat } = getActions();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const {
|
||||||
|
peerId, randomId, additionalInfo, sponsorInfo,
|
||||||
|
} = sponsoredPeer;
|
||||||
|
|
||||||
|
useOnIntersect(ref, observeIntersection, (entry) => {
|
||||||
|
if (entry.intersectionRatio === 1) {
|
||||||
|
viewSponsored({ randomId });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClick = useLastCallback(() => {
|
||||||
|
clickSponsored({ randomId });
|
||||||
|
openChat({ id: peerId });
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
isContextMenuOpen, contextMenuAnchor,
|
||||||
|
handleBeforeContextMenu, handleContextMenu,
|
||||||
|
handleContextMenuClose, handleContextMenuHide,
|
||||||
|
} = useContextMenuHandlers(ref);
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleClick: handleBadgeClick,
|
||||||
|
handleMouseDown: handleBadgeMouseDown,
|
||||||
|
} = useFastClick((e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleContextMenu(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
const buttonRef = useSelectWithEnter(handleClick);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
ref={ref}
|
||||||
|
className="chat-item-clickable search-result"
|
||||||
|
onClick={handleClick}
|
||||||
|
onMouseDown={handleBeforeContextMenu}
|
||||||
|
onContextMenu={handleContextMenu}
|
||||||
|
buttonRef={buttonRef}
|
||||||
|
>
|
||||||
|
{isUserId(peerId) ? (
|
||||||
|
<PrivateChatInfo
|
||||||
|
userId={peerId}
|
||||||
|
withUsername
|
||||||
|
withStory
|
||||||
|
avatarSize="medium"
|
||||||
|
storyViewerOrigin={StoryViewerOrigin.SearchResult}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<GroupChatInfo
|
||||||
|
chatId={peerId}
|
||||||
|
withUsername
|
||||||
|
avatarSize="medium"
|
||||||
|
withStory
|
||||||
|
storyViewerOrigin={StoryViewerOrigin.SearchResult}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<BadgeButton className="search-sponsored-badge" onMouseDown={handleBadgeMouseDown} onClick={handleBadgeClick}>
|
||||||
|
{lang('SponsoredPeerBadge')}
|
||||||
|
<Icon name="more" />
|
||||||
|
</BadgeButton>
|
||||||
|
{contextMenuAnchor && (
|
||||||
|
<SponsoredMessageContextMenuContainer
|
||||||
|
isOpen={isContextMenuOpen}
|
||||||
|
anchor={contextMenuAnchor}
|
||||||
|
triggerRef={ref}
|
||||||
|
randomId={randomId}
|
||||||
|
additionalInfo={additionalInfo}
|
||||||
|
canReport
|
||||||
|
sponsorInfo={sponsorInfo}
|
||||||
|
onClose={handleContextMenuClose}
|
||||||
|
onCloseAnimationEnd={handleContextMenuHide}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(LeftSearchResultSponsored);
|
||||||
@ -122,7 +122,7 @@ const MediaViewer = ({
|
|||||||
toggleChatInfo,
|
toggleChatInfo,
|
||||||
searchChatMediaMessages,
|
searchChatMediaMessages,
|
||||||
loadMoreProfilePhotos,
|
loadMoreProfilePhotos,
|
||||||
clickSponsoredMessage,
|
clickSponsored,
|
||||||
openUrl,
|
openUrl,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ const MediaViewer = ({
|
|||||||
const handleSponsoredClick = useLastCallback((isFromMedia?: boolean) => {
|
const handleSponsoredClick = useLastCallback((isFromMedia?: boolean) => {
|
||||||
if (!sponsoredMessage || !chatId) return;
|
if (!sponsoredMessage || !chatId) return;
|
||||||
|
|
||||||
clickSponsoredMessage({ isMedia: isFromMedia, isFullscreen: true, peerId: chatId });
|
clickSponsored({ isMedia: isFromMedia, isFullscreen: true, randomId: sponsoredMessage.randomId });
|
||||||
openUrl({ url: sponsoredMessage!.url });
|
openUrl({ url: sponsoredMessage!.url });
|
||||||
closeMediaViewer();
|
closeMediaViewer();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,9 +3,6 @@ import React, {
|
|||||||
memo, useRef,
|
memo, useRef,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import type {
|
|
||||||
ApiSponsoredMessage,
|
|
||||||
} from '../../../api/types';
|
|
||||||
import type { IAnchorPosition } from '../../../types';
|
import type { IAnchorPosition } from '../../../types';
|
||||||
|
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
@ -20,7 +17,8 @@ import './MessageContextMenu.scss';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
anchor: IAnchorPosition;
|
anchor: IAnchorPosition;
|
||||||
message: ApiSponsoredMessage;
|
sponsorInfo?: string;
|
||||||
|
canReport?: boolean;
|
||||||
triggerRef: React.RefObject<HTMLElement>;
|
triggerRef: React.RefObject<HTMLElement>;
|
||||||
shouldSkipAbout?: boolean;
|
shouldSkipAbout?: boolean;
|
||||||
onClose: NoneToVoidFunction;
|
onClose: NoneToVoidFunction;
|
||||||
@ -31,9 +29,10 @@ type OwnProps = {
|
|||||||
onSponsoredReport?: NoneToVoidFunction;
|
onSponsoredReport?: NoneToVoidFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SponsoredMessageContextMenu: FC<OwnProps> = ({
|
const SponsoredContextMenu: FC<OwnProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
message,
|
sponsorInfo,
|
||||||
|
canReport,
|
||||||
anchor,
|
anchor,
|
||||||
triggerRef,
|
triggerRef,
|
||||||
shouldSkipAbout,
|
shouldSkipAbout,
|
||||||
@ -53,7 +52,7 @@ const SponsoredMessageContextMenu: FC<OwnProps> = ({
|
|||||||
const getMenuElement = useLastCallback(() => menuRef.current);
|
const getMenuElement = useLastCallback(() => menuRef.current);
|
||||||
const getRootElement = useLastCallback(() => document.body);
|
const getRootElement = useLastCallback(() => document.body);
|
||||||
|
|
||||||
const isSeparatorNeeded = message.sponsorInfo || !shouldSkipAbout || message.canReport;
|
const isSeparatorNeeded = sponsorInfo || !shouldSkipAbout || canReport;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
@ -69,15 +68,15 @@ const SponsoredMessageContextMenu: FC<OwnProps> = ({
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onCloseAnimationEnd={onCloseAnimationEnd}
|
onCloseAnimationEnd={onCloseAnimationEnd}
|
||||||
>
|
>
|
||||||
{message.sponsorInfo && onSponsorInfo && (
|
{sponsorInfo && onSponsorInfo && (
|
||||||
<MenuItem icon="channel" onClick={onSponsorInfo}>{lang('SponsoredMessageSponsor')}</MenuItem>
|
<MenuItem icon="channel" onClick={onSponsorInfo}>{lang('SponsoredMessageSponsor')}</MenuItem>
|
||||||
)}
|
)}
|
||||||
{!shouldSkipAbout && (
|
{!shouldSkipAbout && (
|
||||||
<MenuItem icon="info" onClick={onAboutAdsClick}>
|
<MenuItem icon="info" onClick={onAboutAdsClick}>
|
||||||
{lang(message.canReport ? 'AboutRevenueSharingAds' : 'SponsoredMessageInfo')}
|
{lang(canReport ? 'AboutRevenueSharingAds' : 'SponsoredMessageInfo')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{message.canReport && onSponsoredReport && (
|
{canReport && onSponsoredReport && (
|
||||||
<MenuItem icon="hand-stop" onClick={onSponsoredReport}>
|
<MenuItem icon="hand-stop" onClick={onSponsoredReport}>
|
||||||
{lang('ReportAd')}
|
{lang('ReportAd')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -90,4 +89,4 @@ const SponsoredMessageContextMenu: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(SponsoredMessageContextMenu);
|
export default memo(SponsoredContextMenu);
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
import React from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './SponsoredContextMenuContainer';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const SponsoredContextMenuContainerAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { isOpen } = props;
|
||||||
|
const SponsoredContextMenuContainer = useModuleLoader(
|
||||||
|
Bundles.Extra, 'SponsoredContextMenuContainer', !isOpen,
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return SponsoredContextMenuContainer ? <SponsoredContextMenuContainer {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SponsoredContextMenuContainerAsync;
|
||||||
@ -2,17 +2,19 @@ import type { FC } from '../../../lib/teact/teact';
|
|||||||
import React, { memo } from '../../../lib/teact/teact';
|
import React, { memo } from '../../../lib/teact/teact';
|
||||||
import { getActions } from '../../../global';
|
import { getActions } from '../../../global';
|
||||||
|
|
||||||
import type { ApiSponsoredMessage } from '../../../api/types';
|
|
||||||
import type { IAnchorPosition } from '../../../types';
|
import type { IAnchorPosition } from '../../../types';
|
||||||
|
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useShowTransition from '../../../hooks/useShowTransition';
|
import useShowTransition from '../../../hooks/useShowTransition';
|
||||||
|
|
||||||
import SponsoredMessageContextMenu from './SponsoredMessageContextMenu';
|
import SponsoredContextMenu from './SponsoredContextMenu';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
message: ApiSponsoredMessage;
|
randomId: string;
|
||||||
|
sponsorInfo?: string;
|
||||||
|
additionalInfo?: string;
|
||||||
|
canReport?: boolean;
|
||||||
anchor: IAnchorPosition;
|
anchor: IAnchorPosition;
|
||||||
triggerRef: React.RefObject<HTMLElement>;
|
triggerRef: React.RefObject<HTMLElement>;
|
||||||
shouldSkipAbout?: boolean;
|
shouldSkipAbout?: boolean;
|
||||||
@ -23,7 +25,10 @@ export type OwnProps = {
|
|||||||
|
|
||||||
const SponsoredMessageContextMenuContainer: FC<OwnProps> = ({
|
const SponsoredMessageContextMenuContainer: FC<OwnProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
message,
|
randomId,
|
||||||
|
sponsorInfo,
|
||||||
|
additionalInfo,
|
||||||
|
canReport,
|
||||||
anchor,
|
anchor,
|
||||||
triggerRef,
|
triggerRef,
|
||||||
shouldSkipAbout,
|
shouldSkipAbout,
|
||||||
@ -34,8 +39,8 @@ const SponsoredMessageContextMenuContainer: FC<OwnProps> = ({
|
|||||||
const {
|
const {
|
||||||
openAboutAdsModal,
|
openAboutAdsModal,
|
||||||
showDialog,
|
showDialog,
|
||||||
reportSponsoredMessage,
|
reportSponsored,
|
||||||
hideSponsoredMessages,
|
hideSponsored,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const { ref } = useShowTransition({
|
const { ref } = useShowTransition({
|
||||||
@ -49,26 +54,31 @@ const SponsoredMessageContextMenuContainer: FC<OwnProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleAboutAdsOpen = useLastCallback(() => {
|
const handleAboutAdsOpen = useLastCallback(() => {
|
||||||
openAboutAdsModal({ chatId: message.chatId });
|
openAboutAdsModal({
|
||||||
|
randomId,
|
||||||
|
additionalInfo,
|
||||||
|
canReport,
|
||||||
|
sponsorInfo,
|
||||||
|
});
|
||||||
handleItemClick();
|
handleItemClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSponsoredHide = useLastCallback(() => {
|
const handleSponsoredHide = useLastCallback(() => {
|
||||||
hideSponsoredMessages();
|
hideSponsored();
|
||||||
handleItemClick();
|
handleItemClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSponsorInfo = useLastCallback(() => {
|
const handleSponsorInfo = useLastCallback(() => {
|
||||||
showDialog({
|
showDialog({
|
||||||
data: {
|
data: {
|
||||||
message: [message.sponsorInfo, message.additionalInfo].join('\n'),
|
message: [sponsorInfo, additionalInfo].filter(Boolean).join('\n'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
handleItemClick();
|
handleItemClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleReportSponsoredMessage = useLastCallback(() => {
|
const handleReportSponsoredMessage = useLastCallback(() => {
|
||||||
reportSponsoredMessage({ peerId: message.chatId, randomId: message.randomId });
|
reportSponsored({ randomId });
|
||||||
handleItemClick();
|
handleItemClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -78,11 +88,12 @@ const SponsoredMessageContextMenuContainer: FC<OwnProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className="ContextMenuContainer">
|
<div ref={ref} className="ContextMenuContainer">
|
||||||
<SponsoredMessageContextMenu
|
<SponsoredContextMenu
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
anchor={anchor}
|
anchor={anchor}
|
||||||
triggerRef={triggerRef}
|
triggerRef={triggerRef}
|
||||||
message={message}
|
canReport={canReport}
|
||||||
|
sponsorInfo={sponsorInfo}
|
||||||
shouldSkipAbout={shouldSkipAbout}
|
shouldSkipAbout={shouldSkipAbout}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onCloseAnimationEnd={onClose}
|
onCloseAnimationEnd={onClose}
|
||||||
@ -38,7 +38,7 @@ import PeerColorWrapper from '../../common/PeerColorWrapper';
|
|||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import MessageAppendix from './MessageAppendix';
|
import MessageAppendix from './MessageAppendix';
|
||||||
import Photo from './Photo';
|
import Photo from './Photo';
|
||||||
import SponsoredMessageContextMenuContainer from './SponsoredMessageContextMenuContainer.async';
|
import SponsoredContextMenuContainer from './SponsoredContextMenuContainer.async';
|
||||||
import Video from './Video';
|
import Video from './Video';
|
||||||
|
|
||||||
import './SponsoredMessage.scss';
|
import './SponsoredMessage.scss';
|
||||||
@ -72,10 +72,10 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
|
|||||||
canAutoPlayMedia,
|
canAutoPlayMedia,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
viewSponsoredMessage,
|
viewSponsored,
|
||||||
openUrl,
|
openUrl,
|
||||||
hideSponsoredMessages,
|
hideSponsored,
|
||||||
clickSponsoredMessage,
|
clickSponsored,
|
||||||
openMediaViewer,
|
openMediaViewer,
|
||||||
openAboutAdsModal,
|
openAboutAdsModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
@ -103,11 +103,11 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return shouldObserve ? observeIntersection(contentRef.current!, (target) => {
|
return shouldObserve ? observeIntersection(contentRef.current!, (target) => {
|
||||||
if (target.isIntersecting) {
|
if (target.isIntersecting && message?.randomId) {
|
||||||
viewSponsoredMessage({ peerId: chatId });
|
viewSponsored({ randomId: message.randomId });
|
||||||
}
|
}
|
||||||
}) : undefined;
|
}) : undefined;
|
||||||
}, [chatId, shouldObserve, observeIntersection, viewSponsoredMessage]);
|
}, [message?.randomId, shouldObserve, observeIntersection, viewSponsored]);
|
||||||
|
|
||||||
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||||
preventMessageInputBlur(e);
|
preventMessageInputBlur(e);
|
||||||
@ -115,7 +115,7 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleHideSponsoredMessage = useLastCallback(() => {
|
const handleHideSponsoredMessage = useLastCallback(() => {
|
||||||
hideSponsoredMessages();
|
hideSponsored();
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -128,12 +128,13 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
|
|||||||
const handleClick = useLastCallback(() => {
|
const handleClick = useLastCallback(() => {
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
|
|
||||||
clickSponsoredMessage({ isMedia: photo || isGif ? true : undefined, peerId: chatId });
|
clickSponsored({ randomId: message.randomId, isMedia: photo || isGif ? true : undefined });
|
||||||
openUrl({ url: message.url, shouldSkipModal: true });
|
openUrl({ url: message.url, shouldSkipModal: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleOpenMedia = useLastCallback(() => {
|
const handleOpenMedia = useLastCallback(() => {
|
||||||
clickSponsoredMessage({ isMedia: true, peerId: chatId });
|
if (!message) return;
|
||||||
|
clickSponsored({ randomId: message.randomId, isMedia: true });
|
||||||
openMediaViewer({
|
openMediaViewer({
|
||||||
origin: MediaViewerOrigin.SponsoredMessage,
|
origin: MediaViewerOrigin.SponsoredMessage,
|
||||||
chatId,
|
chatId,
|
||||||
@ -142,7 +143,13 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleOpenAboutAdsModal = useLastCallback(() => {
|
const handleOpenAboutAdsModal = useLastCallback(() => {
|
||||||
openAboutAdsModal({ chatId });
|
if (!message) return;
|
||||||
|
openAboutAdsModal({
|
||||||
|
randomId: message.randomId,
|
||||||
|
canReport: message.canReport,
|
||||||
|
additionalInfo: message.additionalInfo,
|
||||||
|
sponsorInfo: message.sponsorInfo,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const extraPadding = 0;
|
const extraPadding = 0;
|
||||||
@ -315,11 +322,14 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{contextMenuAnchor && (
|
{contextMenuAnchor && (
|
||||||
<SponsoredMessageContextMenuContainer
|
<SponsoredContextMenuContainer
|
||||||
isOpen={isContextMenuOpen}
|
isOpen={isContextMenuOpen}
|
||||||
anchor={contextMenuAnchor}
|
anchor={contextMenuAnchor}
|
||||||
triggerRef={ref}
|
triggerRef={ref}
|
||||||
message={message!}
|
randomId={message.randomId}
|
||||||
|
canReport={message.canReport}
|
||||||
|
sponsorInfo={message.sponsorInfo}
|
||||||
|
additionalInfo={message.additionalInfo}
|
||||||
onClose={handleContextMenuClose}
|
onClose={handleContextMenuClose}
|
||||||
onCloseAnimationEnd={handleContextMenuHide}
|
onCloseAnimationEnd={handleContextMenuHide}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
import type { FC } from '../../../lib/teact/teact';
|
|
||||||
import React from '../../../lib/teact/teact';
|
|
||||||
|
|
||||||
import type { OwnProps } from './SponsoredMessageContextMenuContainer';
|
|
||||||
|
|
||||||
import { Bundles } from '../../../util/moduleLoader';
|
|
||||||
|
|
||||||
import useModuleLoader from '../../../hooks/useModuleLoader';
|
|
||||||
|
|
||||||
const SponsoredMessageContextMenuContainerAsync: FC<OwnProps> = (props) => {
|
|
||||||
const { isOpen } = props;
|
|
||||||
const SponsoredMessageContextMenuContainer = useModuleLoader(
|
|
||||||
Bundles.Extra, 'SponsoredMessageContextMenuContainer', !isOpen,
|
|
||||||
);
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
||||||
return SponsoredMessageContextMenuContainer ? <SponsoredMessageContextMenuContainer {...props} /> : undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SponsoredMessageContextMenuContainerAsync;
|
|
||||||
@ -17,7 +17,7 @@ import useHeaderPane, { type PaneState } from '../hooks/useHeaderPane';
|
|||||||
|
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import BadgeButton from '../../common/BadgeButton';
|
import BadgeButton from '../../common/BadgeButton';
|
||||||
import SponsoredMessageContextMenuContainer from '../message/SponsoredMessageContextMenuContainer';
|
import SponsoredMessageContextMenuContainer from '../message/SponsoredContextMenuContainer';
|
||||||
|
|
||||||
import styles from './BotAdPane.module.scss';
|
import styles from './BotAdPane.module.scss';
|
||||||
|
|
||||||
@ -40,9 +40,9 @@ const BotAdPane = ({
|
|||||||
onPaneStateChange,
|
onPaneStateChange,
|
||||||
}: OwnProps & StateProps) => {
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
viewSponsoredMessage,
|
viewSponsored,
|
||||||
openUrl,
|
openUrl,
|
||||||
clickSponsoredMessage,
|
clickSponsored,
|
||||||
openAboutAdsModal,
|
openAboutAdsModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
@ -67,25 +67,38 @@ const BotAdPane = ({
|
|||||||
const handleClick = useLastCallback(() => {
|
const handleClick = useLastCallback(() => {
|
||||||
if (!renderingSponsoredMessage) return;
|
if (!renderingSponsoredMessage) return;
|
||||||
|
|
||||||
clickSponsoredMessage({ peerId: chatId });
|
clickSponsored({ randomId: renderingSponsoredMessage.randomId });
|
||||||
openUrl({ url: renderingSponsoredMessage.url, shouldSkipModal: true });
|
openUrl({ url: renderingSponsoredMessage.url, shouldSkipModal: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleAboutClick = useLastCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
const handleAboutClick = useLastCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
if (!renderingSponsoredMessage) return;
|
||||||
|
const {
|
||||||
|
randomId, additionalInfo, canReport, sponsorInfo,
|
||||||
|
} = renderingSponsoredMessage;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
openAboutAdsModal({ chatId });
|
openAboutAdsModal({
|
||||||
|
randomId,
|
||||||
|
additionalInfo,
|
||||||
|
canReport,
|
||||||
|
sponsorInfo,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shouldRender && sponsoredMessage) {
|
if (shouldRender && renderingSponsoredMessage) {
|
||||||
viewSponsoredMessage({ peerId: chatId });
|
viewSponsored({ randomId: renderingSponsoredMessage.randomId });
|
||||||
}
|
}
|
||||||
}, [shouldRender, sponsoredMessage, chatId]);
|
}, [shouldRender, renderingSponsoredMessage, chatId]);
|
||||||
|
|
||||||
if (!shouldRender || !renderingSponsoredMessage) {
|
if (!shouldRender || !renderingSponsoredMessage) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
randomId, canReport, additionalInfo, sponsorInfo,
|
||||||
|
} = renderingSponsoredMessage;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
peerColor,
|
peerColor,
|
||||||
content,
|
content,
|
||||||
@ -132,7 +145,10 @@ const BotAdPane = ({
|
|||||||
isOpen={isContextMenuOpen}
|
isOpen={isContextMenuOpen}
|
||||||
anchor={contextMenuAnchor}
|
anchor={contextMenuAnchor}
|
||||||
triggerRef={ref}
|
triggerRef={ref}
|
||||||
message={renderingSponsoredMessage}
|
randomId={randomId}
|
||||||
|
additionalInfo={additionalInfo}
|
||||||
|
canReport={canReport}
|
||||||
|
sponsorInfo={sponsorInfo}
|
||||||
onClose={handleContextMenuClose}
|
onClose={handleContextMenuClose}
|
||||||
onCloseAnimationEnd={handleContextMenuHide}
|
onCloseAnimationEnd={handleContextMenuHide}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import React, { memo, useMemo, useRef } from '../../../lib/teact/teact';
|
import React, { memo, useMemo, useRef } from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiSponsoredMessage } from '../../../api/types';
|
|
||||||
import type { TabState } from '../../../global/types';
|
import type { TabState } from '../../../global/types';
|
||||||
import type { TableAboutData } from '../common/TableAboutModal';
|
import type { TableAboutData } from '../common/TableAboutModal';
|
||||||
|
|
||||||
import { selectSponsoredMessage } from '../../../global/selectors';
|
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
|
||||||
@ -16,7 +14,7 @@ import useOldLang from '../../../hooks/useOldLang';
|
|||||||
|
|
||||||
import Icon from '../../common/icons/Icon';
|
import Icon from '../../common/icons/Icon';
|
||||||
import SafeLink from '../../common/SafeLink';
|
import SafeLink from '../../common/SafeLink';
|
||||||
import SponsoredMessageContextMenuContainer from '../../middle/message/SponsoredMessageContextMenuContainer';
|
import SponsoredMessageContextMenuContainer from '../../middle/message/SponsoredContextMenuContainer';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import Modal from '../../ui/Modal';
|
import Modal from '../../ui/Modal';
|
||||||
import TableAboutModal from '../common/TableAboutModal';
|
import TableAboutModal from '../common/TableAboutModal';
|
||||||
@ -29,18 +27,21 @@ export type OwnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
message?: ApiSponsoredMessage;
|
|
||||||
minLevelToRestrictAds?: number;
|
minLevelToRestrictAds?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AboutAdsModal = ({ message, minLevelToRestrictAds }: OwnProps & StateProps) => {
|
const AboutAdsModal = ({ modal, minLevelToRestrictAds }: OwnProps & StateProps) => {
|
||||||
const { closeAboutAdsModal } = getActions();
|
const { closeAboutAdsModal } = getActions();
|
||||||
|
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const moreMenuRef = useRef<HTMLButtonElement>(null);
|
const moreMenuRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const isOpen = Boolean(message);
|
const isOpen = Boolean(modal);
|
||||||
const isMonetizationSharing = message?.canReport;
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
const {
|
||||||
|
canReport, randomId, additionalInfo, sponsorInfo,
|
||||||
|
} = renderingModal || {};
|
||||||
|
const isMonetizationSharing = canReport;
|
||||||
|
|
||||||
const renderingIsNewDesign = useCurrentOrPrev(isMonetizationSharing);
|
const renderingIsNewDesign = useCurrentOrPrev(isMonetizationSharing);
|
||||||
|
|
||||||
@ -139,12 +140,15 @@ const AboutAdsModal = ({ message, minLevelToRestrictAds }: OwnProps & StateProps
|
|||||||
buttonText={oldLang('RevenueSharingAdsUnderstood')}
|
buttonText={oldLang('RevenueSharingAdsUnderstood')}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
/>
|
/>
|
||||||
{contextMenuAnchor && message && (
|
{contextMenuAnchor && randomId && (
|
||||||
<SponsoredMessageContextMenuContainer
|
<SponsoredMessageContextMenuContainer
|
||||||
isOpen={isContextMenuOpen}
|
isOpen={isContextMenuOpen}
|
||||||
anchor={contextMenuAnchor}
|
anchor={contextMenuAnchor}
|
||||||
triggerRef={moreMenuRef}
|
triggerRef={moreMenuRef}
|
||||||
message={message}
|
randomId={randomId}
|
||||||
|
additionalInfo={additionalInfo}
|
||||||
|
canReport={canReport}
|
||||||
|
sponsorInfo={sponsorInfo}
|
||||||
shouldSkipAbout
|
shouldSkipAbout
|
||||||
onItemClick={handleClose}
|
onItemClick={handleClose}
|
||||||
onClose={handleContextMenuClose}
|
onClose={handleContextMenuClose}
|
||||||
@ -174,12 +178,10 @@ const AboutAdsModal = ({ message, minLevelToRestrictAds }: OwnProps & StateProps
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, { modal }): StateProps => {
|
(global): StateProps => {
|
||||||
const message = modal?.chatId ? selectSponsoredMessage(global, modal.chatId) : undefined;
|
|
||||||
const minLevelToRestrictAds = global.appConfig?.channelRestrictAdsLevelMin;
|
const minLevelToRestrictAds = global.appConfig?.channelRestrictAdsLevelMin;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message,
|
|
||||||
minLevelToRestrictAds,
|
minLevelToRestrictAds,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const ReportAdModal = ({
|
|||||||
modal,
|
modal,
|
||||||
}: OwnProps) => {
|
}: OwnProps) => {
|
||||||
const {
|
const {
|
||||||
reportSponsoredMessage, closeReportAdModal, openPreviousReportAdModal,
|
reportSponsored, closeReportAdModal, openPreviousReportAdModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
const isOpen = Boolean(modal);
|
const isOpen = Boolean(modal);
|
||||||
@ -40,7 +40,7 @@ const ReportAdModal = ({
|
|||||||
|
|
||||||
const handleOptionClick = useLastCallback((e, option: string) => {
|
const handleOptionClick = useLastCallback((e, option: string) => {
|
||||||
const { chatId, randomId } = modal!;
|
const { chatId, randomId } = modal!;
|
||||||
reportSponsoredMessage({ peerId: chatId, randomId, option });
|
reportSponsored({ peerId: chatId, randomId, option });
|
||||||
});
|
});
|
||||||
|
|
||||||
const [renderingSection, renderingDepth] = useMemo(() => {
|
const [renderingSection, renderingDepth] = useMemo(() => {
|
||||||
|
|||||||
@ -33,11 +33,14 @@ addActionHandler('setGlobalSearchQuery', (global, actions, payload): ActionRetur
|
|||||||
|
|
||||||
if (query && !chatId) {
|
if (query && !chatId) {
|
||||||
void searchThrottled(async () => {
|
void searchThrottled(async () => {
|
||||||
const result = await callApi('searchChats', { query });
|
const [searchResult, sponsoredResult] = await Promise.all([
|
||||||
|
callApi('searchChats', { query }),
|
||||||
|
callApi('fetchSponsoredPeer', { query }),
|
||||||
|
]);
|
||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
const currentSearchQuery = selectCurrentGlobalSearchQuery(global, tabId);
|
const currentSearchQuery = selectCurrentGlobalSearchQuery(global, tabId);
|
||||||
if (!result || !currentSearchQuery || (query !== currentSearchQuery)) {
|
if (!searchResult || !currentSearchQuery || (query !== currentSearchQuery)) {
|
||||||
global = updateGlobalSearchFetchingStatus(global, { chats: false }, tabId);
|
global = updateGlobalSearchFetchingStatus(global, { chats: false }, tabId);
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
return;
|
return;
|
||||||
@ -45,7 +48,7 @@ addActionHandler('setGlobalSearchQuery', (global, actions, payload): ActionRetur
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
accountResultIds, globalResultIds,
|
accountResultIds, globalResultIds,
|
||||||
} = result;
|
} = searchResult;
|
||||||
|
|
||||||
global = updateGlobalSearchFetchingStatus(global, { chats: false }, tabId);
|
global = updateGlobalSearchFetchingStatus(global, { chats: false }, tabId);
|
||||||
global = updateGlobalSearch(global, {
|
global = updateGlobalSearch(global, {
|
||||||
@ -56,6 +59,7 @@ addActionHandler('setGlobalSearchQuery', (global, actions, payload): ActionRetur
|
|||||||
...selectTabState(global, tabId).globalSearch.globalResults,
|
...selectTabState(global, tabId).globalSearch.globalResults,
|
||||||
peerIds: globalResultIds,
|
peerIds: globalResultIds,
|
||||||
},
|
},
|
||||||
|
sponsoredPeer: sponsoredResult,
|
||||||
}, tabId);
|
}, tabId);
|
||||||
|
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|||||||
@ -84,6 +84,7 @@ import {
|
|||||||
updateChat,
|
updateChat,
|
||||||
updateChatFullInfo,
|
updateChatFullInfo,
|
||||||
updateChatMessage,
|
updateChatMessage,
|
||||||
|
updateGlobalSearch,
|
||||||
updateListedIds,
|
updateListedIds,
|
||||||
updateMessageTranslation,
|
updateMessageTranslation,
|
||||||
updateOutlyingLists,
|
updateOutlyingLists,
|
||||||
@ -135,7 +136,6 @@ import {
|
|||||||
selectReplyCanBeSentToChat,
|
selectReplyCanBeSentToChat,
|
||||||
selectScheduledMessage,
|
selectScheduledMessage,
|
||||||
selectSendAs,
|
selectSendAs,
|
||||||
selectSponsoredMessage,
|
|
||||||
selectTabState,
|
selectTabState,
|
||||||
selectThreadIdFromMessage,
|
selectThreadIdFromMessage,
|
||||||
selectTopic,
|
selectTopic,
|
||||||
@ -1867,38 +1867,24 @@ addActionHandler('loadSponsoredMessages', async (global, actions, payload): Prom
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('viewSponsoredMessage', (global, actions, payload): ActionReturnType => {
|
addActionHandler('viewSponsored', (global, actions, payload): ActionReturnType => {
|
||||||
const { peerId } = payload;
|
const { randomId } = payload;
|
||||||
const peer = selectPeer(global, peerId);
|
|
||||||
const message = selectSponsoredMessage(global, peerId);
|
|
||||||
if (!peer || !message) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void callApi('viewSponsoredMessage', { random: message.randomId });
|
void callApi('viewSponsoredMessage', { random: randomId });
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('clickSponsoredMessage', (global, actions, payload): ActionReturnType => {
|
addActionHandler('clickSponsored', (global, actions, payload): ActionReturnType => {
|
||||||
const { peerId, isMedia, isFullscreen } = payload;
|
const { randomId, isMedia, isFullscreen } = payload;
|
||||||
const peer = selectPeer(global, peerId);
|
|
||||||
const message = selectSponsoredMessage(global, peerId);
|
|
||||||
if (!peer || !message) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void callApi('clickSponsoredMessage', {
|
void callApi('clickSponsoredMessage', {
|
||||||
random: message.randomId, isMedia, isFullscreen,
|
random: randomId, isMedia, isFullscreen,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('reportSponsoredMessage', async (global, actions, payload): Promise<void> => {
|
addActionHandler('reportSponsored', async (global, actions, payload): Promise<void> => {
|
||||||
const {
|
const {
|
||||||
peerId, randomId, option = '', tabId = getCurrentTabId(),
|
peerId, randomId, option = '', tabId = getCurrentTabId(),
|
||||||
} = payload;
|
} = payload;
|
||||||
const peer = selectPeer(global, peerId);
|
|
||||||
if (!peer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await callApi('reportSponsoredMessage', { randomId, option });
|
const result = await callApi('reportSponsoredMessage', { randomId, option });
|
||||||
|
|
||||||
@ -1918,7 +1904,13 @@ addActionHandler('reportSponsoredMessage', async (global, actions, payload): Pro
|
|||||||
actions.closeReportAdModal({ tabId });
|
actions.closeReportAdModal({ tabId });
|
||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
global = deleteSponsoredMessage(global, peerId);
|
if (peerId) {
|
||||||
|
global = deleteSponsoredMessage(global, peerId);
|
||||||
|
} else {
|
||||||
|
global = updateGlobalSearch(global, {
|
||||||
|
sponsoredPeer: undefined,
|
||||||
|
}, tabId);
|
||||||
|
}
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1943,7 +1935,7 @@ addActionHandler('reportSponsoredMessage', async (global, actions, payload): Pro
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('hideSponsoredMessages', async (global, actions, payload): Promise<void> => {
|
addActionHandler('hideSponsored', async (global, actions, payload): Promise<void> => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
|
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
|
||||||
if (!isCurrentUserPremium) {
|
if (!isCurrentUserPremium) {
|
||||||
|
|||||||
@ -1064,11 +1064,16 @@ addActionHandler('closeDeleteMessageModal', (global, actions, payload): ActionRe
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('openAboutAdsModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openAboutAdsModal', (global, actions, payload): ActionReturnType => {
|
||||||
const { chatId, tabId = getCurrentTabId() } = payload || {};
|
const {
|
||||||
|
randomId, additionalInfo, canReport, sponsorInfo, tabId = getCurrentTabId(),
|
||||||
|
} = payload || {};
|
||||||
|
|
||||||
return updateTabState(global, {
|
return updateTabState(global, {
|
||||||
aboutAdsModal: {
|
aboutAdsModal: {
|
||||||
chatId,
|
randomId,
|
||||||
|
canReport,
|
||||||
|
additionalInfo,
|
||||||
|
sponsorInfo,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -504,21 +504,24 @@ export interface ActionPayloads {
|
|||||||
loadSponsoredMessages: {
|
loadSponsoredMessages: {
|
||||||
peerId: string;
|
peerId: string;
|
||||||
};
|
};
|
||||||
viewSponsoredMessage: {
|
viewSponsored: {
|
||||||
peerId: string;
|
randomId: string;
|
||||||
};
|
};
|
||||||
clickSponsoredMessage: {
|
clickSponsored: {
|
||||||
peerId: string;
|
randomId: string;
|
||||||
isMedia?: boolean;
|
isMedia?: boolean;
|
||||||
isFullscreen?: boolean;
|
isFullscreen?: boolean;
|
||||||
};
|
};
|
||||||
reportSponsoredMessage: {
|
reportSponsored: {
|
||||||
peerId: string;
|
peerId?: string;
|
||||||
randomId: string;
|
randomId: string;
|
||||||
option?: string;
|
option?: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
openAboutAdsModal: {
|
openAboutAdsModal: {
|
||||||
chatId: string;
|
randomId: string;
|
||||||
|
canReport?: boolean;
|
||||||
|
sponsorInfo?: string;
|
||||||
|
additionalInfo?: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeAboutAdsModal: WithTabId | undefined;
|
closeAboutAdsModal: WithTabId | undefined;
|
||||||
openPreparedInlineMessageModal: {
|
openPreparedInlineMessageModal: {
|
||||||
@ -542,7 +545,7 @@ export interface ActionPayloads {
|
|||||||
openPreviousReportModal: WithTabId | undefined;
|
openPreviousReportModal: WithTabId | undefined;
|
||||||
closeReportAdModal: WithTabId | undefined;
|
closeReportAdModal: WithTabId | undefined;
|
||||||
closeReportModal: WithTabId | undefined;
|
closeReportModal: WithTabId | undefined;
|
||||||
hideSponsoredMessages: WithTabId | undefined;
|
hideSponsored: WithTabId | undefined;
|
||||||
loadSendAs: {
|
loadSendAs: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import type {
|
|||||||
ApiReceiptRegular,
|
ApiReceiptRegular,
|
||||||
ApiSavedGifts,
|
ApiSavedGifts,
|
||||||
ApiSavedStarGift,
|
ApiSavedStarGift,
|
||||||
|
ApiSponsoredPeer,
|
||||||
ApiStarGift,
|
ApiStarGift,
|
||||||
ApiStarGiftAttribute,
|
ApiStarGiftAttribute,
|
||||||
ApiStarGiveawayOption,
|
ApiStarGiveawayOption,
|
||||||
@ -186,7 +187,10 @@ export type TabState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
aboutAdsModal?: {
|
aboutAdsModal?: {
|
||||||
chatId: string;
|
randomId: string;
|
||||||
|
canReport?: boolean;
|
||||||
|
sponsorInfo?: string;
|
||||||
|
additionalInfo?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
reactionPicker?: {
|
reactionPicker?: {
|
||||||
@ -220,6 +224,7 @@ export type TabState = {
|
|||||||
currentContent?: GlobalSearchContent;
|
currentContent?: GlobalSearchContent;
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
foundTopicIds?: number[];
|
foundTopicIds?: number[];
|
||||||
|
sponsoredPeer?: ApiSponsoredPeer;
|
||||||
fetchingStatus?: {
|
fetchingStatus?: {
|
||||||
chats?: boolean;
|
chats?: boolean;
|
||||||
messages?: boolean;
|
messages?: boolean;
|
||||||
@ -440,7 +445,7 @@ export type TabState = {
|
|||||||
openedCustomEmojiSetIds?: string[];
|
openedCustomEmojiSetIds?: string[];
|
||||||
|
|
||||||
reportAdModal?: {
|
reportAdModal?: {
|
||||||
chatId: string;
|
chatId?: string;
|
||||||
randomId: string;
|
randomId: string;
|
||||||
sections: {
|
sections: {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@ -1498,6 +1498,7 @@ contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.
|
|||||||
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
|
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
|
||||||
contacts.resolvePhone#8af94344 phone:string = contacts.ResolvedPeer;
|
contacts.resolvePhone#8af94344 phone:string = contacts.ResolvedPeer;
|
||||||
contacts.editCloseFriends#ba6705f0 id:Vector<long> = Bool;
|
contacts.editCloseFriends#ba6705f0 id:Vector<long> = Bool;
|
||||||
|
contacts.getSponsoredPeers#b6c8c393 q:string = contacts.SponsoredPeers;
|
||||||
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
||||||
messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs;
|
messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs;
|
||||||
messages.getHistory#4423e6c5 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
|
messages.getHistory#4423e6c5 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages;
|
||||||
|
|||||||
@ -77,6 +77,7 @@
|
|||||||
"contacts.addContact",
|
"contacts.addContact",
|
||||||
"contacts.resolvePhone",
|
"contacts.resolvePhone",
|
||||||
"contacts.editCloseFriends",
|
"contacts.editCloseFriends",
|
||||||
|
"contacts.getSponsoredPeers",
|
||||||
"messages.getMessages",
|
"messages.getMessages",
|
||||||
"messages.getDialogs",
|
"messages.getDialogs",
|
||||||
"messages.getHistory",
|
"messages.getHistory",
|
||||||
|
|||||||
1
src/types/language.d.ts
vendored
1
src/types/language.d.ts
vendored
@ -685,6 +685,7 @@ export interface LangPair {
|
|||||||
'MessageRecommendedLabel': undefined;
|
'MessageRecommendedLabel': undefined;
|
||||||
'SponsoredMessageAd': undefined;
|
'SponsoredMessageAd': undefined;
|
||||||
'SponsoredMessageAdWhatIsThis': undefined;
|
'SponsoredMessageAdWhatIsThis': undefined;
|
||||||
|
'SponsoredPeerBadge': undefined;
|
||||||
'PremiumStickerTooltip': undefined;
|
'PremiumStickerTooltip': undefined;
|
||||||
'ViewAction': undefined;
|
'ViewAction': undefined;
|
||||||
'Loading': undefined;
|
'Loading': undefined;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user