From d0735fcc0e585de0de4ff5db3a31e85054379396 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Mon, 17 Oct 2022 17:35:07 +0200 Subject: [PATCH] Composer / Send As: Display lock icon for non-premium users (#2076) --- src/api/gramjs/apiBuilders/chats.ts | 8 ++++ src/api/gramjs/methods/messages.ts | 9 ++--- src/api/types/chats.ts | 7 +++- src/components/middle/composer/Composer.tsx | 24 ++++++------ .../middle/composer/SendAsMenu.scss | 4 ++ src/components/middle/composer/SendAsMenu.tsx | 39 +++++++++++++------ src/components/ui/Notification.scss | 6 +-- src/config.ts | 2 +- src/global/actions/api/messages.ts | 4 +- 9 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/api/gramjs/apiBuilders/chats.ts b/src/api/gramjs/apiBuilders/chats.ts index 605461251..db6cd2599 100644 --- a/src/api/gramjs/apiBuilders/chats.ts +++ b/src/api/gramjs/apiBuilders/chats.ts @@ -11,6 +11,7 @@ import type { ApiExportedInvite, ApiChatInviteImporter, ApiChatSettings, + ApiSendAsPeerId, } from '../../types'; import { pick, pickTruthy } from '../../../util/iteratees'; import { @@ -467,3 +468,10 @@ export function buildApiChatReactions(availableReactions?: GramJs.TypeChatReacti return undefined; } + +export function buildApiSendAsPeerId(sendAs: GramJs.SendAsPeer): ApiSendAsPeerId { + return { + id: getApiChatIdFromMtpPeer(sendAs.peer), + isPremium: sendAs.premiumRequired, + }; +} diff --git a/src/api/gramjs/methods/messages.ts b/src/api/gramjs/methods/messages.ts index e10fb4008..df5d65d71 100644 --- a/src/api/gramjs/methods/messages.ts +++ b/src/api/gramjs/methods/messages.ts @@ -55,7 +55,7 @@ import { buildInputPollFromExisting, } from '../gramjsBuilders'; import localDb from '../localDb'; -import { buildApiChatFromPreview } from '../apiBuilders/chats'; +import { buildApiChatFromPreview, buildApiSendAsPeerId } from '../apiBuilders/chats'; import { fetchFile } from '../../../util/files'; import { addEntitiesWithPhotosToLocalDb, @@ -65,7 +65,6 @@ import { } from '../helpers'; import { interpolateArray } from '../../../util/waveform'; import { requestChatUpdate } from './chats'; -import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; const FAST_SEND_TIMEOUT = 1000; const INPUT_WAVEFORM_LENGTH = 63; @@ -1311,13 +1310,13 @@ export async function fetchSendAs({ addEntitiesWithPhotosToLocalDb(result.users); addEntitiesWithPhotosToLocalDb(result.chats); - const users = result.users.map(buildApiUser).filter(Boolean as any); - const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean as any); + const users = result.users.map(buildApiUser).filter(Boolean); + const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter(Boolean); return { users, chats, - ids: result.peers.map((sendAsPeer) => getApiChatIdFromMtpPeer(sendAsPeer.peer)), + sendAs: result.peers.map(buildApiSendAsPeerId), }; } diff --git a/src/api/types/chats.ts b/src/api/types/chats.ts index 2fa707ff2..6c23d9c36 100644 --- a/src/api/types/chats.ts +++ b/src/api/types/chats.ts @@ -68,7 +68,7 @@ export interface ApiChat { joinRequests?: ApiChatInviteImporter[]; isJoinToSend?: boolean; isJoinRequest?: boolean; - sendAsIds?: string[]; + sendAsPeerIds?: ApiSendAsPeerId[]; unreadReactions?: number[]; unreadMentions?: number[]; @@ -182,3 +182,8 @@ export interface ApiChatSettings { canAddContact?: boolean; canBlockContact?: boolean; } + +export interface ApiSendAsPeerId { + id: string; + isPremium?: boolean; +} diff --git a/src/components/middle/composer/Composer.tsx b/src/components/middle/composer/Composer.tsx index 7d3998e55..2aa8fd0ff 100644 --- a/src/components/middle/composer/Composer.tsx +++ b/src/components/middle/composer/Composer.tsx @@ -282,8 +282,9 @@ const Composer: FC = ({ const lastMessageSendTimeSeconds = useRef(); const prevDropAreaState = usePrevious(dropAreaState); const { width: windowWidth } = windowSize.get(); - const sendAsIds = chat?.sendAsIds; - const canShowSendAs = sendAsIds && (sendAsIds.length > 1 || !sendAsIds.includes(currentUserId!)); + const sendAsPeerIds = chat?.sendAsPeerIds; + const canShowSendAs = sendAsPeerIds + && (sendAsPeerIds.length > 1 || !sendAsPeerIds.some((peer) => peer.id === currentUserId!)); // Prevent Symbol Menu from closing when calendar is open const [isSymbolMenuForced, forceShowSymbolMenu, cancelForceShowSymbolMenu] = useFlag(); const sendMessageAction = useSendMessageAction(chatId, threadId); @@ -304,10 +305,10 @@ const Composer: FC = ({ }, [isReady, chatId, loadScheduledHistory, lastSyncTime, threadId]); useEffect(() => { - if (chatId && chat && lastSyncTime && !sendAsIds && isReady && isChatSuperGroup(chat)) { + if (chatId && chat && lastSyncTime && !sendAsPeerIds && isReady && isChatSuperGroup(chat)) { loadSendAs({ chatId }); } - }, [chat, chatId, isReady, lastSyncTime, loadSendAs, sendAsIds]); + }, [chat, chatId, isReady, lastSyncTime, loadSendAs, sendAsPeerIds]); useEffect(() => { if (chatId && chat && lastSyncTime && !chat.fullInfo && isReady && isChatSuperGroup(chat)) { @@ -316,10 +317,10 @@ const Composer: FC = ({ }, [chat, chatId, isReady, lastSyncTime, loadFullChat]); const shouldAnimateSendAsButtonRef = useRef(false); - useOnChange(([prevChatId, prevSendAsIds]) => { - // We only animate send-as button if `sendAsIds` was missing when opening the chat - shouldAnimateSendAsButtonRef.current = Boolean(chatId === prevChatId && sendAsIds && !prevSendAsIds); - }, [chatId, sendAsIds]); + useOnChange(([prevChatId, prevSendAsPeerIds]) => { + // We only animate send-as button if `sendAsPeerIds` was missing when opening the chat + shouldAnimateSendAsButtonRef.current = Boolean(chatId === prevChatId && sendAsPeerIds && !prevSendAsPeerIds); + }, [chatId, sendAsPeerIds]); useLayoutEffect(() => { if (!appendixRef.current) return; @@ -1056,7 +1057,8 @@ const Composer: FC = ({ onClose={closeSendAsMenu} chatId={chatId} selectedSendAsId={sendAsId} - sendAsIds={sendAsIds} + sendAsPeerIds={sendAsPeerIds} + isCurrentUserPremium={isCurrentUserPremium} /> ( const keyboardMessage = botKeyboardMessageId ? selectChatMessage(global, chatId, botKeyboardMessageId) : undefined; const { currentUserId } = global; const defaultSendAsId = chat?.fullInfo ? chat?.fullInfo?.sendAsId || currentUserId : undefined; - const sendAsId = chat?.sendAsIds && defaultSendAsId && chat.sendAsIds.includes(defaultSendAsId) - ? defaultSendAsId + const sendAsId = chat?.sendAsPeerIds && defaultSendAsId + && chat.sendAsPeerIds.some((peer) => peer.id === defaultSendAsId) ? defaultSendAsId : (chat?.adminRights?.anonymous ? chat?.id : undefined); const sendAsUser = sendAsId ? selectUser(global, sendAsId) : undefined; const sendAsChat = !sendAsUser && sendAsId ? selectChat(global, sendAsId) : undefined; diff --git a/src/components/middle/composer/SendAsMenu.scss b/src/components/middle/composer/SendAsMenu.scss index 674e415c8..ac305956a 100644 --- a/src/components/middle/composer/SendAsMenu.scss +++ b/src/components/middle/composer/SendAsMenu.scss @@ -59,6 +59,10 @@ padding: 0.5625rem 1rem !important; border-radius: 0; align-items: center; + + & > .send-as-icon-locked { + margin-right: 0.5rem; + } } .info { diff --git a/src/components/middle/composer/SendAsMenu.tsx b/src/components/middle/composer/SendAsMenu.tsx index 3e43d6a4b..b8d26b45b 100644 --- a/src/components/middle/composer/SendAsMenu.tsx +++ b/src/components/middle/composer/SendAsMenu.tsx @@ -1,8 +1,10 @@ -import type { FC } from '../../../lib/teact/teact'; import React, { useCallback, useEffect, useRef, memo, } from '../../../lib/teact/teact'; +import type { FC } from '../../../lib/teact/teact'; +import type { ApiSendAsPeerId } from '../../../api/types'; + import setTooltipItemVisible from '../../../util/setTooltipItemVisible'; import { useKeyboardNavigation } from './hooks/useKeyboardNavigation'; import { IS_TOUCH_ENV } from '../../../util/environment'; @@ -21,20 +23,22 @@ import './SendAsMenu.scss'; export type OwnProps = { isOpen: boolean; - onClose: () => void; chatId?: string; selectedSendAsId?: string; - sendAsIds?: string[]; + sendAsPeerIds?: ApiSendAsPeerId[]; + isCurrentUserPremium?: boolean; + onClose: () => void; }; const SendAsMenu: FC = ({ isOpen, - onClose, chatId, selectedSendAsId, - sendAsIds, + sendAsPeerIds, + isCurrentUserPremium, + onClose, }) => { - const { saveDefaultSendAs } = getActions(); + const { saveDefaultSendAs, showNotification, openPremiumModal } = getActions(); // No need for expensive global updates on users and chats, so we avoid them const usersById = getGlobal().users.byId; @@ -59,7 +63,7 @@ const SendAsMenu: FC = ({ const selectedSendAsIndex = useKeyboardNavigation({ isActive: isOpen, - items: sendAsIds, + items: sendAsPeerIds, onSelect: handleUserSelect, shouldSelectOnTab: true, shouldSaveSelectionOnUpdateItems: true, @@ -71,10 +75,10 @@ const SendAsMenu: FC = ({ }, [selectedSendAsIndex]); useEffect(() => { - if (sendAsIds && !sendAsIds.length) { + if (sendAsPeerIds && !sendAsPeerIds.length) { onClose(); } - }, [sendAsIds, onClose]); + }, [sendAsPeerIds, onClose]); return ( = ({ noCompact >
{lang('SendMessageAsTitle')}
- {usersById && chatsById && sendAsIds?.map((id, index) => { + {usersById && chatsById && sendAsPeerIds?.map(({ id, isPremium }, index) => { const user = isUserId(id) ? usersById[id] : undefined; const chat = !user ? chatsById[id] : undefined; const userOrChat = user || chat; + const handleClick = () => { + if (!isPremium || isCurrentUserPremium) { + handleUserSelect(id); + } else { + showNotification({ + message: lang('SelectSendAsPeerPremiumHint'), + actionText: lang('Open'), + action: () => openPremiumModal(), + }); + } + }; + return ( handleUserSelect(id)} + onClick={handleClick} focus={selectedSendAsIndex === index} + rightElement={!isCurrentUserPremium && isPremium && } > { const result = await callApi('fetchSendAs', { chat }); if (!result) { setGlobal(updateChat(getGlobal(), chatId, { - sendAsIds: [], + sendAsPeerIds: [], })); return; @@ -1100,7 +1100,7 @@ addActionHandler('loadSendAs', async (global, actions, payload) => { global = getGlobal(); global = addUsers(global, buildCollectionByKey(result.users, 'id')); global = addChats(global, buildCollectionByKey(result.chats, 'id')); - global = updateChat(global, chatId, { sendAsIds: result.ids }); + global = updateChat(global, chatId, { sendAsPeerIds: result.sendAs }); setGlobal(global); });