Composer / Send As: Display lock icon for non-premium users (#2076)

This commit is contained in:
Alexander Zinchuk 2022-10-17 17:35:07 +02:00
parent 04dee2ce7b
commit d0735fcc0e
9 changed files with 69 additions and 34 deletions

View File

@ -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,
};
}

View File

@ -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<ApiUser>(Boolean as any);
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter<ApiChat>(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),
};
}

View File

@ -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;
}

View File

@ -282,8 +282,9 @@ const Composer: FC<OwnProps & StateProps> = ({
const lastMessageSendTimeSeconds = useRef<number>();
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<OwnProps & StateProps> = ({
}, [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<OwnProps & StateProps> = ({
}, [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<OwnProps & StateProps> = ({
onClose={closeSendAsMenu}
chatId={chatId}
selectedSendAsId={sendAsId}
sendAsIds={sendAsIds}
sendAsPeerIds={sendAsPeerIds}
isCurrentUserPremium={isCurrentUserPremium}
/>
<MentionTooltip
isOpen={isMentionTooltipOpen}
@ -1318,8 +1320,8 @@ export default memo(withGlobal<OwnProps>(
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;

View File

@ -59,6 +59,10 @@
padding: 0.5625rem 1rem !important;
border-radius: 0;
align-items: center;
& > .send-as-icon-locked {
margin-right: 0.5rem;
}
}
.info {

View File

@ -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<OwnProps> = ({
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<OwnProps> = ({
const selectedSendAsIndex = useKeyboardNavigation({
isActive: isOpen,
items: sendAsIds,
items: sendAsPeerIds,
onSelect: handleUserSelect,
shouldSelectOnTab: true,
shouldSaveSelectionOnUpdateItems: true,
@ -71,10 +75,10 @@ const SendAsMenu: FC<OwnProps> = ({
}, [selectedSendAsIndex]);
useEffect(() => {
if (sendAsIds && !sendAsIds.length) {
if (sendAsPeerIds && !sendAsPeerIds.length) {
onClose();
}
}, [sendAsIds, onClose]);
}, [sendAsPeerIds, onClose]);
return (
<Menu
@ -90,18 +94,31 @@ const SendAsMenu: FC<OwnProps> = ({
noCompact
>
<div className="send-as-title" dir="auto">{lang('SendMessageAsTitle')}</div>
{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 (
<ListItem
key={id}
className="SendAsItem chat-item-clickable scroll-item with-avatar"
// eslint-disable-next-line react/jsx-no-bind
onClick={() => handleUserSelect(id)}
onClick={handleClick}
focus={selectedSendAsIndex === index}
rightElement={!isCurrentUserPremium && isPremium && <i className="icon-lock-badge send-as-icon-locked" />}
>
<Avatar
size="small"

View File

@ -1,6 +1,6 @@
.Notification-container {
position: relative;
width: 24rem;
width: 27rem;
max-width: 100vw;
margin: 4.25rem auto 0.25rem;
z-index: var(--z-notification);
@ -61,8 +61,8 @@
color: var(--color-primary);
font-weight: 500;
text-transform: none;
flex: 1;
padding: 0 2rem;
flex-shrink: 1;
padding: 0.25rem;
height: 2rem;
}
}

View File

@ -42,7 +42,7 @@ export const MEDIA_PROGRESSIVE_CACHE_DISABLED = false;
export const MEDIA_PROGRESSIVE_CACHE_NAME = 'tt-media-progressive';
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
export const LANG_CACHE_NAME = 'tt-lang-packs-v13';
export const LANG_CACHE_NAME = 'tt-lang-packs-v14';
export const ASSET_CACHE_NAME = 'tt-assets';
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];

View File

@ -1091,7 +1091,7 @@ addActionHandler('loadSendAs', async (global, actions, payload) => {
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);
});