Privacy Settings: Implement premium category (#4536)
This commit is contained in:
parent
d64ed45af8
commit
cf8eaf270e
@ -1,4 +1,5 @@
|
||||
.Avatar {
|
||||
--premium-gradient: linear-gradient(88.39deg, #6C93FF -2.56%, #976FFF 51.27%, #DF69D1 107.39%);
|
||||
--color-user: var(--color-primary);
|
||||
--radius: 50%;
|
||||
|
||||
@ -258,7 +259,7 @@
|
||||
--color-user: var(--color-deleted-account);
|
||||
}
|
||||
|
||||
&.unknown-user {
|
||||
background: var(--premium-gradient);
|
||||
&.premium-gradient-bg > .inner {
|
||||
background-image: var(--premium-gradient);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import type {
|
||||
ApiChat, ApiPeer, ApiPhoto, ApiUser,
|
||||
} from '../../api/types';
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
import type { StoryViewerOrigin } from '../../types';
|
||||
import type { CustomPeer, StoryViewerOrigin } from '../../types';
|
||||
import { ApiMediaFormat } from '../../api/types';
|
||||
|
||||
import { IS_TEST } from '../../config';
|
||||
@ -49,11 +49,10 @@ cn.icon = cn('icon');
|
||||
type OwnProps = {
|
||||
className?: string;
|
||||
size?: AvatarSize;
|
||||
peer?: ApiPeer;
|
||||
peer?: ApiPeer | CustomPeer;
|
||||
photo?: ApiPhoto;
|
||||
text?: string;
|
||||
isSavedMessages?: boolean;
|
||||
isUnknownUser?: boolean;
|
||||
isSavedDialog?: boolean;
|
||||
withVideo?: boolean;
|
||||
withStory?: boolean;
|
||||
@ -78,7 +77,6 @@ const Avatar: FC<OwnProps> = ({
|
||||
text,
|
||||
isSavedMessages,
|
||||
isSavedDialog,
|
||||
isUnknownUser,
|
||||
withVideo,
|
||||
withStory,
|
||||
forPremiumPromo,
|
||||
@ -97,12 +95,14 @@ const Avatar: FC<OwnProps> = ({
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const videoLoopCountRef = useRef(0);
|
||||
const isPeerChat = peer && 'title' in peer;
|
||||
const isCustomPeer = peer && 'isCustomPeer' in peer;
|
||||
const realPeer = peer && !isCustomPeer ? peer : undefined;
|
||||
const isPeerChat = realPeer && 'title' in realPeer;
|
||||
const user = peer && !isPeerChat ? peer as ApiUser : undefined;
|
||||
const chat = peer && isPeerChat ? peer as ApiChat : undefined;
|
||||
const isDeleted = user && isDeletedUser(user);
|
||||
const isReplies = peer && isChatWithRepliesBot(peer.id);
|
||||
const isAnonymousForwards = peer && isAnonymousForwardsChat(peer.id);
|
||||
const isReplies = realPeer && isChatWithRepliesBot(realPeer.id);
|
||||
const isAnonymousForwards = realPeer && isAnonymousForwardsChat(realPeer.id);
|
||||
const isForum = chat?.isForum;
|
||||
let imageHash: string | undefined;
|
||||
let videoHash: string | undefined;
|
||||
@ -112,7 +112,7 @@ const Avatar: FC<OwnProps> = ({
|
||||
const shouldFetchBig = size === 'jumbo';
|
||||
if (!isSavedMessages && !isDeleted) {
|
||||
if ((user && !noPersonalPhoto) || chat) {
|
||||
imageHash = getChatAvatarHash(peer!, shouldFetchBig ? 'big' : undefined);
|
||||
imageHash = getChatAvatarHash(peer as ApiPeer, shouldFetchBig ? 'big' : undefined);
|
||||
} else if (photo) {
|
||||
imageHash = `photo${photo.id}?size=m`;
|
||||
if (photo.isVideo && withVideo) {
|
||||
@ -122,8 +122,8 @@ const Avatar: FC<OwnProps> = ({
|
||||
}
|
||||
|
||||
const specialIcon = useMemo(() => {
|
||||
if (isUnknownUser) {
|
||||
return 'user';
|
||||
if (isCustomPeer) {
|
||||
return peer.avatarIcon;
|
||||
}
|
||||
|
||||
if (isSavedMessages) {
|
||||
@ -143,7 +143,7 @@ const Avatar: FC<OwnProps> = ({
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [isUnknownUser, isSavedMessages, isDeleted, isReplies, isAnonymousForwards, isSavedDialog]);
|
||||
}, [isCustomPeer, isSavedMessages, isDeleted, isReplies, isAnonymousForwards, peer, isSavedDialog]);
|
||||
|
||||
const imgBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
||||
const videoBlobUrl = useMedia(videoHash, !shouldLoadVideo, ApiMediaFormat.BlobUrl);
|
||||
@ -215,23 +215,25 @@ const Avatar: FC<OwnProps> = ({
|
||||
content = getFirstLetters(text, 2);
|
||||
}
|
||||
|
||||
const isRoundedRect = isForum && !((withStory || withStorySolid) && peer?.hasStories);
|
||||
const isRoundedRect = (isCustomPeer && peer.isAvatarSquare)
|
||||
|| (isForum && !((withStory || withStorySolid) && realPeer?.hasStories));
|
||||
const isPremiumGradient = isCustomPeer && peer.withPremiumGradient;
|
||||
|
||||
const fullClassName = buildClassName(
|
||||
`Avatar size-${size}`,
|
||||
className,
|
||||
getPeerColorClass(peer),
|
||||
isUnknownUser && 'unknown-user',
|
||||
!peer && text && 'hidden-user',
|
||||
isSavedMessages && 'saved-messages',
|
||||
isAnonymousForwards && 'anonymous-forwards',
|
||||
isDeleted && 'deleted-account',
|
||||
isReplies && 'replies-bot-account',
|
||||
isPremiumGradient && 'premium-gradient-bg',
|
||||
isRoundedRect && 'forum',
|
||||
((withStory && peer?.hasStories) || forPremiumPromo) && 'with-story-circle',
|
||||
withStorySolid && peer?.hasStories && 'with-story-solid',
|
||||
((withStory && realPeer?.hasStories) || forPremiumPromo) && 'with-story-circle',
|
||||
withStorySolid && realPeer?.hasStories && 'with-story-solid',
|
||||
withStorySolid && forceFriendStorySolid && 'close-friend',
|
||||
withStorySolid && (peer?.hasUnreadStories || forceUnreadStorySolid) && 'has-unread-story',
|
||||
withStorySolid && (realPeer?.hasUnreadStories || forceUnreadStorySolid) && 'has-unread-story',
|
||||
onClick && 'interactive',
|
||||
(!isSavedMessages && !imgBlobUrl) && 'no-photo',
|
||||
);
|
||||
@ -239,11 +241,11 @@ const Avatar: FC<OwnProps> = ({
|
||||
const hasMedia = Boolean(isSavedMessages || imgBlobUrl);
|
||||
|
||||
const { handleClick, handleMouseDown } = useFastClick((e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
if (withStory && storyViewerMode !== 'disabled' && peer?.hasStories) {
|
||||
if (withStory && storyViewerMode !== 'disabled' && realPeer?.hasStories) {
|
||||
e.stopPropagation();
|
||||
|
||||
openStoryViewer({
|
||||
peerId: peer.id,
|
||||
peerId: realPeer.id,
|
||||
isSinglePeer: storyViewerMode === 'single-peer',
|
||||
origin: storyViewerOrigin,
|
||||
});
|
||||
@ -259,9 +261,9 @@ const Avatar: FC<OwnProps> = ({
|
||||
<div
|
||||
ref={ref}
|
||||
className={fullClassName}
|
||||
id={peer?.id && withStory ? getPeerStoryHtmlId(peer.id) : undefined}
|
||||
data-peer-id={peer?.id}
|
||||
data-test-sender-id={IS_TEST ? peer?.id : undefined}
|
||||
id={realPeer?.id && withStory ? getPeerStoryHtmlId(realPeer.id) : undefined}
|
||||
data-peer-id={realPeer?.id}
|
||||
data-test-sender-id={IS_TEST ? realPeer?.id : undefined}
|
||||
aria-label={typeof content === 'string' ? author : undefined}
|
||||
onClick={handleClick}
|
||||
onMouseDown={handleMouseDown}
|
||||
@ -269,8 +271,8 @@ const Avatar: FC<OwnProps> = ({
|
||||
<div className="inner">
|
||||
{typeof content === 'string' ? renderText(content, [size === 'jumbo' ? 'hq_emoji' : 'emoji']) : content}
|
||||
</div>
|
||||
{withStory && peer?.hasStories && (
|
||||
<AvatarStoryCircle peerId={peer.id} size={size} withExtraGap={withStoryGap} />
|
||||
{withStory && realPeer?.hasStories && (
|
||||
<AvatarStoryCircle peerId={realPeer.id} size={size} withExtraGap={withStoryGap} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -2,8 +2,11 @@ import type { FC } from '../../lib/teact/teact';
|
||||
import React, { memo, useMemo } from '../../lib/teact/teact';
|
||||
import { getActions } from '../../global';
|
||||
|
||||
import type { ApiChat, ApiPeer, ApiUser } from '../../api/types';
|
||||
import type {
|
||||
ApiChat, ApiPeer, ApiUser,
|
||||
} from '../../api/types';
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
import type { CustomPeer } from '../../types';
|
||||
|
||||
import { EMOJI_STATUS_LOOP_LIMIT } from '../../config';
|
||||
import {
|
||||
@ -25,7 +28,7 @@ import VerifiedIcon from './VerifiedIcon';
|
||||
import styles from './FullNameTitle.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
peer?: ApiPeer;
|
||||
peer: ApiPeer | CustomPeer;
|
||||
className?: string;
|
||||
noVerified?: boolean;
|
||||
noFake?: boolean;
|
||||
@ -34,7 +37,6 @@ type OwnProps = {
|
||||
isSavedMessages?: boolean;
|
||||
isSavedDialog?: boolean;
|
||||
noLoopLimit?: boolean;
|
||||
isUnknownUser?: boolean;
|
||||
canCopyTitle?: boolean;
|
||||
onEmojiStatusClick?: NoneToVoidFunction;
|
||||
observeIntersection?: ObserveFn;
|
||||
@ -55,25 +57,15 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
onEmojiStatusClick,
|
||||
observeIntersection,
|
||||
iconElement,
|
||||
isUnknownUser,
|
||||
}) => {
|
||||
const lang = useLang();
|
||||
const { showNotification } = getActions();
|
||||
const isUser = peer && isUserId(peer.id);
|
||||
const realPeer = 'id' in peer ? peer : undefined;
|
||||
const customPeer = 'isCustomPeer' in peer ? peer : undefined;
|
||||
const isUser = realPeer && isUserId(realPeer.id);
|
||||
const title = realPeer && (isUser ? getUserFullName(realPeer as ApiUser) : getChatTitle(lang, realPeer as ApiChat));
|
||||
const isPremium = isUser && (peer as ApiUser).isPremium;
|
||||
|
||||
const title = useMemo(() => {
|
||||
if (isUnknownUser) {
|
||||
return lang('BoostingToBeDistributed');
|
||||
}
|
||||
|
||||
if (peer && isUserId(peer.id)) {
|
||||
return getUserFullName(peer as ApiUser);
|
||||
}
|
||||
|
||||
return peer && getChatTitle(lang, peer as ApiChat);
|
||||
}, [isUnknownUser, lang, peer]);
|
||||
|
||||
const handleTitleClick = useLastCallback((e) => {
|
||||
if (!title || !canCopyTitle) {
|
||||
return;
|
||||
@ -85,20 +77,24 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
});
|
||||
|
||||
const specialTitle = useMemo(() => {
|
||||
if (customPeer) {
|
||||
return lang(customPeer.titleKey);
|
||||
}
|
||||
|
||||
if (isSavedMessages) {
|
||||
return lang(isSavedDialog ? 'MyNotes' : 'SavedMessages');
|
||||
}
|
||||
|
||||
if (peer && isAnonymousForwardsChat(peer.id)) {
|
||||
if (isAnonymousForwardsChat(realPeer!.id)) {
|
||||
return lang('AnonymousForward');
|
||||
}
|
||||
|
||||
if (peer && isChatWithRepliesBot(peer.id)) {
|
||||
if (isChatWithRepliesBot(realPeer!.id)) {
|
||||
return lang('RepliesTitle');
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [isSavedDialog, isSavedMessages, lang, peer]);
|
||||
}, [customPeer, isSavedDialog, isSavedMessages, lang, realPeer]);
|
||||
|
||||
if (specialTitle) {
|
||||
return (
|
||||
@ -120,18 +116,18 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
</h3>
|
||||
{!iconElement && peer && (
|
||||
<>
|
||||
{!noVerified && peer?.isVerified && <VerifiedIcon />}
|
||||
{!noFake && peer?.fakeType && <FakeIcon fakeType={peer.fakeType} />}
|
||||
{withEmojiStatus && peer.emojiStatus && (
|
||||
{!noVerified && realPeer?.isVerified && <VerifiedIcon />}
|
||||
{!noFake && realPeer?.fakeType && <FakeIcon fakeType={realPeer.fakeType} />}
|
||||
{withEmojiStatus && realPeer?.emojiStatus && (
|
||||
<CustomEmoji
|
||||
documentId={peer.emojiStatus.documentId}
|
||||
documentId={realPeer.emojiStatus.documentId}
|
||||
size={emojiStatusSize}
|
||||
loopLimit={!noLoopLimit ? EMOJI_STATUS_LOOP_LIMIT : undefined}
|
||||
observeIntersectionForLoading={observeIntersection}
|
||||
onClick={onEmojiStatusClick}
|
||||
/>
|
||||
)}
|
||||
{withEmojiStatus && !peer.emojiStatus && isPremium && <PremiumIcon />}
|
||||
{withEmojiStatus && !realPeer?.emojiStatus && isPremium && <PremiumIcon />}
|
||||
</>
|
||||
)}
|
||||
{iconElement}
|
||||
|
||||
@ -29,6 +29,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.picker-category-title {
|
||||
color: var(--color-text-secondary);
|
||||
padding-inline: 1rem;
|
||||
font-weight: 500;
|
||||
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid var(--color-borders);
|
||||
padding-top: 0.75rem;
|
||||
margin-top: 0.375rem;
|
||||
}
|
||||
}
|
||||
|
||||
.picker-list {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useEffect, useMemo, useRef,
|
||||
memo, useCallback, useEffect, useMemo, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import type { ApiCountry } from '../../api/types';
|
||||
import type { CustomPeer, CustomPeerType } from '../../types';
|
||||
|
||||
import { requestMeasure } from '../../lib/fasterdom/fasterdom';
|
||||
import { isUserId } from '../../global/helpers';
|
||||
@ -27,7 +28,9 @@ import './Picker.scss';
|
||||
|
||||
type OwnProps = {
|
||||
className?: string;
|
||||
categories?: CustomPeer[];
|
||||
itemIds: string[];
|
||||
selectedCategories?: CustomPeerType[];
|
||||
selectedIds: string[];
|
||||
lockedSelectedIds?: string[];
|
||||
lockedUnselectedIds?: string[];
|
||||
@ -42,6 +45,7 @@ type OwnProps = {
|
||||
isRoundCheckbox?: boolean;
|
||||
forceShowSelf?: boolean;
|
||||
isViewOnly?: boolean;
|
||||
onSelectedCategoriesChange?: (categories: CustomPeerType[]) => void;
|
||||
onSelectedIdsChange?: (ids: string[]) => void;
|
||||
onFilterChange?: (value: string) => void;
|
||||
onDisabledClick?: (id: string, isSelected: boolean) => void;
|
||||
@ -58,7 +62,9 @@ const ALWAYS_FULL_ITEMS_COUNT = 5;
|
||||
|
||||
const Picker: FC<OwnProps> = ({
|
||||
className,
|
||||
categories,
|
||||
itemIds,
|
||||
selectedCategories,
|
||||
selectedIds,
|
||||
filterValue,
|
||||
filterPlaceholder,
|
||||
@ -73,6 +79,7 @@ const Picker: FC<OwnProps> = ({
|
||||
lockedUnselectedSubtitle,
|
||||
forceShowSelf,
|
||||
isViewOnly,
|
||||
onSelectedCategoriesChange,
|
||||
onSelectedIdsChange,
|
||||
onFilterChange,
|
||||
onDisabledClick,
|
||||
@ -100,7 +107,16 @@ const Picker: FC<OwnProps> = ({
|
||||
return selectedIds.filter((id) => !lockedSelectedIdsSet.has(id));
|
||||
}, [lockedSelectedIdsSet, selectedIds]);
|
||||
|
||||
const categoriesByType = useMemo(() => {
|
||||
if (!categories) return {};
|
||||
return buildCollectionByKey(categories, 'type');
|
||||
}, [categories]);
|
||||
|
||||
const sortedItemIds = useMemo(() => {
|
||||
if (filterValue) {
|
||||
return itemIds;
|
||||
}
|
||||
|
||||
const lockedSelectedBucket: string[] = [];
|
||||
const unlockedBucket: string[] = [];
|
||||
const lockedUnselectableBucket: string[] = [];
|
||||
@ -115,8 +131,8 @@ const Picker: FC<OwnProps> = ({
|
||||
}
|
||||
});
|
||||
|
||||
return lockedSelectedBucket.concat(unlockedBucket).concat(lockedUnselectableBucket);
|
||||
}, [itemIds, lockedSelectedIdsSet, lockedUnselectedIdsSet]);
|
||||
return lockedSelectedBucket.concat(unlockedBucket, lockedUnselectableBucket);
|
||||
}, [filterValue, itemIds, lockedSelectedIdsSet, lockedUnselectedIdsSet]);
|
||||
|
||||
const handleItemClick = useLastCallback((id: string) => {
|
||||
if (lockedSelectedIdsSet.has(id)) {
|
||||
@ -129,13 +145,24 @@ const Picker: FC<OwnProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const newSelectedIds = selectedIds.slice();
|
||||
if (newSelectedIds.includes(id)) {
|
||||
newSelectedIds.splice(newSelectedIds.indexOf(id), 1);
|
||||
if (categoriesByType[id]) {
|
||||
const categoryType = categoriesByType[id].type;
|
||||
const newSelectedCategories = selectedCategories?.slice() || [];
|
||||
if (newSelectedCategories.includes(categoryType)) {
|
||||
newSelectedCategories.splice(newSelectedCategories.indexOf(categoryType), 1);
|
||||
} else {
|
||||
newSelectedCategories.push(categoryType);
|
||||
}
|
||||
onSelectedCategoriesChange?.(newSelectedCategories);
|
||||
} else {
|
||||
newSelectedIds.push(id);
|
||||
const newSelectedIds = selectedIds.slice();
|
||||
if (newSelectedIds.includes(id)) {
|
||||
newSelectedIds.splice(newSelectedIds.indexOf(id), 1);
|
||||
} else {
|
||||
newSelectedIds.push(id);
|
||||
}
|
||||
onSelectedIdsChange?.(newSelectedIds);
|
||||
}
|
||||
onSelectedIdsChange?.(newSelectedIds);
|
||||
onFilterChange?.('');
|
||||
});
|
||||
|
||||
@ -153,7 +180,15 @@ const Picker: FC<OwnProps> = ({
|
||||
return buildCollectionByKey(countryList, 'iso2');
|
||||
}, [countryList]);
|
||||
|
||||
const renderChatInfo = (id: string) => {
|
||||
const renderCategory = useLastCallback((category: CustomPeer) => {
|
||||
return (
|
||||
<PrivateChatInfo
|
||||
customPeer={category}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const renderChatInfo = useLastCallback((id: string) => {
|
||||
const isUnselectable = lockedUnselectedIdsSet.has(id);
|
||||
if (isCountryList && countriesByIso) {
|
||||
const country = countriesByIso[id];
|
||||
@ -169,12 +204,69 @@ const Picker: FC<OwnProps> = ({
|
||||
} else {
|
||||
return <GroupChatInfo chatId={id} status={isUnselectable ? lockedUnselectedSubtitle : undefined} />;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const renderItem = useCallback((id: string, isCategory?: boolean) => {
|
||||
const category = isCategory ? categoriesByType[id] : undefined;
|
||||
const shouldRenderLockIcon = lockedUnselectedIdsSet.has(id);
|
||||
const isLocked = lockedSelectedIdsSet.has(id) || shouldRenderLockIcon;
|
||||
const isChecked = category ? selectedCategories?.includes(category.type) : selectedIds.includes(id);
|
||||
const renderCheckbox = () => {
|
||||
return (isViewOnly || shouldRenderLockIcon) ? undefined : (
|
||||
<Checkbox
|
||||
label=""
|
||||
disabled={isLocked}
|
||||
checked={isChecked}
|
||||
round={isRoundCheckbox}
|
||||
/>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<ListItem
|
||||
key={id}
|
||||
className={buildClassName('chat-item-clickable picker-list-item', isRoundCheckbox && 'chat-item')}
|
||||
disabled={isLocked}
|
||||
inactive={isViewOnly}
|
||||
allowDisabledClick={Boolean(onDisabledClick)}
|
||||
secondaryIcon={shouldRenderLockIcon ? 'lock-badge' : undefined}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleItemClick(id)}
|
||||
ripple
|
||||
>
|
||||
{!isRoundCheckbox ? renderCheckbox() : undefined}
|
||||
{category ? renderCategory(category) : renderChatInfo(id)}
|
||||
{isRoundCheckbox ? renderCheckbox() : undefined}
|
||||
</ListItem>
|
||||
);
|
||||
}, [
|
||||
categoriesByType, isRoundCheckbox, isViewOnly, lockedSelectedIdsSet, lockedUnselectedIdsSet,
|
||||
onDisabledClick, renderChatInfo, selectedCategories, selectedIds,
|
||||
]);
|
||||
|
||||
const beforeChildren = useMemo(() => {
|
||||
return (
|
||||
<div key="categories">
|
||||
{Boolean(categories?.length) && (
|
||||
<div className="picker-category-title">{lang('PrivacyUserTypes')}</div>
|
||||
)}
|
||||
{categories?.map((category) => renderItem(category.type, true))}
|
||||
<div className="picker-category-title">{lang('FilterChats')}</div>
|
||||
</div>
|
||||
);
|
||||
}, [categories, lang, renderItem]);
|
||||
|
||||
return (
|
||||
<div className={buildClassName('Picker', className)}>
|
||||
{isSearchable && (
|
||||
<div className="picker-header custom-scroll" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
{selectedCategories?.map((category) => (
|
||||
<PickerSelectedItem
|
||||
customPeer={categoriesByType[category]}
|
||||
onClick={handleItemClick}
|
||||
clickArg={category}
|
||||
canClose
|
||||
/>
|
||||
))}
|
||||
{lockedSelectedIds?.map((id, i) => (
|
||||
<PickerSelectedItem
|
||||
peerId={id}
|
||||
@ -209,40 +301,11 @@ const Picker: FC<OwnProps> = ({
|
||||
<InfiniteScroll
|
||||
className={buildClassName('picker-list', 'custom-scroll', isRoundCheckbox && 'withRoundedCheckbox')}
|
||||
items={viewportIds}
|
||||
beforeChildren={beforeChildren}
|
||||
onLoadMore={getMore}
|
||||
noScrollRestore={noScrollRestore}
|
||||
>
|
||||
{viewportIds.map((id) => {
|
||||
const shouldRenderLockIcon = lockedUnselectedIdsSet.has(id);
|
||||
const isLocked = lockedSelectedIdsSet.has(id) || shouldRenderLockIcon;
|
||||
const renderCheckbox = () => {
|
||||
return (isViewOnly || shouldRenderLockIcon) ? undefined : (
|
||||
<Checkbox
|
||||
label=""
|
||||
disabled={isLocked}
|
||||
checked={selectedIds.includes(id)}
|
||||
round={isRoundCheckbox}
|
||||
/>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<ListItem
|
||||
key={id}
|
||||
className={buildClassName('chat-item-clickable picker-list-item', isRoundCheckbox && 'chat-item')}
|
||||
disabled={isLocked}
|
||||
inactive={isViewOnly}
|
||||
allowDisabledClick={Boolean(onDisabledClick)}
|
||||
secondaryIcon={shouldRenderLockIcon ? 'lock-badge' : undefined}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleItemClick(id)}
|
||||
ripple
|
||||
>
|
||||
{!isRoundCheckbox ? renderCheckbox() : undefined}
|
||||
{renderChatInfo(id)}
|
||||
{isRoundCheckbox ? renderCheckbox() : undefined}
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
{viewportIds.map((id) => renderItem(id))}
|
||||
</InfiniteScroll>
|
||||
) : !isLoading && viewportIds && !viewportIds.length ? (
|
||||
<p className="no-results">{notFoundText || 'Sorry, nothing found.'}</p>
|
||||
|
||||
@ -125,7 +125,7 @@
|
||||
}
|
||||
|
||||
|
||||
&.forum-avatar {
|
||||
&.square-avatar {
|
||||
border-start-start-radius: 0.625rem;
|
||||
border-end-start-radius: 0.625rem;
|
||||
--border-radius-forum-avatar: 0.625rem;
|
||||
|
||||
@ -3,6 +3,7 @@ import React, { memo } from '../../lib/teact/teact';
|
||||
import { withGlobal } from '../../global';
|
||||
|
||||
import type { ApiChat, ApiUser } from '../../api/types';
|
||||
import type { CustomPeer } from '../../types';
|
||||
import type { IconName } from '../../types/icons';
|
||||
|
||||
import { getChatTitle, getUserFirstOrLastName } from '../../global/helpers';
|
||||
@ -14,11 +15,13 @@ import renderText from './helpers/renderText';
|
||||
import useLang from '../../hooks/useLang';
|
||||
|
||||
import Avatar from './Avatar';
|
||||
import Icon from './Icon';
|
||||
|
||||
import './PickerSelectedItem.scss';
|
||||
|
||||
type OwnProps = {
|
||||
peerId?: string;
|
||||
customPeer?: CustomPeer;
|
||||
icon?: IconName;
|
||||
title?: string;
|
||||
isMinimized?: boolean;
|
||||
@ -45,6 +48,7 @@ const PickerSelectedItem: FC<OwnProps & StateProps> = ({
|
||||
clickArg,
|
||||
chat,
|
||||
user,
|
||||
customPeer,
|
||||
className,
|
||||
fluid,
|
||||
isSavedMessages,
|
||||
@ -59,23 +63,24 @@ const PickerSelectedItem: FC<OwnProps & StateProps> = ({
|
||||
if (icon && title) {
|
||||
iconElement = (
|
||||
<div className="item-icon">
|
||||
<i className={buildClassName('icon', `icon-${icon}`)} />
|
||||
<Icon name={icon} />
|
||||
</div>
|
||||
);
|
||||
|
||||
titleText = title;
|
||||
} else if (user || chat) {
|
||||
} else if (customPeer || user || chat) {
|
||||
iconElement = (
|
||||
<Avatar
|
||||
peer={user || chat}
|
||||
peer={customPeer || user || chat}
|
||||
size="small"
|
||||
isSavedMessages={isSavedMessages}
|
||||
/>
|
||||
);
|
||||
|
||||
const name = !chat || (user && !isSavedMessages)
|
||||
? getUserFirstOrLastName(user)
|
||||
: getChatTitle(lang, chat, isSavedMessages);
|
||||
const name = (customPeer && lang(customPeer.titleKey))
|
||||
|| (!chat || (user && !isSavedMessages)
|
||||
? getUserFirstOrLastName(user)
|
||||
: getChatTitle(lang, chat, isSavedMessages));
|
||||
|
||||
titleText = name ? renderText(name) : undefined;
|
||||
}
|
||||
@ -83,11 +88,11 @@ const PickerSelectedItem: FC<OwnProps & StateProps> = ({
|
||||
const fullClassName = buildClassName(
|
||||
'PickerSelectedItem',
|
||||
className,
|
||||
chat?.isForum && 'forum-avatar',
|
||||
(chat?.isForum || customPeer?.isAvatarSquare) && 'square-avatar',
|
||||
isMinimized && 'minimized',
|
||||
canClose && 'closeable',
|
||||
fluid && 'fluid',
|
||||
withPeerColors && getPeerColorClass(chat || user),
|
||||
withPeerColors && getPeerColorClass(customPeer || chat || user),
|
||||
);
|
||||
|
||||
return (
|
||||
@ -105,7 +110,7 @@ const PickerSelectedItem: FC<OwnProps & StateProps> = ({
|
||||
)}
|
||||
{canClose && (
|
||||
<div className="item-remove">
|
||||
<i className="icon icon-close" />
|
||||
<Icon name="close" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -5,7 +5,7 @@ import { getActions, withGlobal } from '../../global';
|
||||
import type {
|
||||
ApiChatMember, ApiTypingStatus, ApiUser, ApiUserStatus,
|
||||
} from '../../api/types';
|
||||
import type { StoryViewerOrigin } from '../../types';
|
||||
import type { CustomPeer, StoryViewerOrigin } from '../../types';
|
||||
import type { IconName } from '../../types/icons';
|
||||
import { MediaViewerOrigin } from '../../types';
|
||||
|
||||
@ -27,7 +27,8 @@ import Icon from './Icon';
|
||||
import TypingStatus from './TypingStatus';
|
||||
|
||||
type OwnProps = {
|
||||
userId: string;
|
||||
userId?: string;
|
||||
customPeer?: CustomPeer;
|
||||
typingStatus?: ApiTypingStatus;
|
||||
avatarSize?: 'tiny' | 'small' | 'medium' | 'large' | 'jumbo';
|
||||
forceShowSelf?: boolean;
|
||||
@ -38,7 +39,6 @@ type OwnProps = {
|
||||
withMediaViewer?: boolean;
|
||||
withUsername?: boolean;
|
||||
withStory?: boolean;
|
||||
isUnknownUser?: boolean;
|
||||
withFullInfo?: boolean;
|
||||
withUpdatingStatus?: boolean;
|
||||
storyViewerOrigin?: StoryViewerOrigin;
|
||||
@ -67,6 +67,7 @@ type StateProps =
|
||||
};
|
||||
|
||||
const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
customPeer,
|
||||
typingStatus,
|
||||
avatarSize = 'medium',
|
||||
status,
|
||||
@ -82,7 +83,6 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
noEmojiStatus,
|
||||
noFake,
|
||||
noVerified,
|
||||
isUnknownUser,
|
||||
noRtl,
|
||||
user,
|
||||
userStatus,
|
||||
@ -131,7 +131,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const mainUsername = useMemo(() => user && withUsername && getMainUsername(user), [user, withUsername]);
|
||||
|
||||
if (!user && !isUnknownUser) {
|
||||
if (!user && !customPeer) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -153,6 +153,14 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (customPeer?.subtitleKey) {
|
||||
return (
|
||||
<span className="status" dir="auto">
|
||||
<span className="user-status" dir="auto">{lang(customPeer.subtitleKey)}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return undefined;
|
||||
}
|
||||
@ -194,7 +202,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<FullNameTitle
|
||||
peer={user!}
|
||||
peer={customPeer || user!}
|
||||
noFake={noFake}
|
||||
noVerified={noVerified}
|
||||
withEmojiStatus={!noEmojiStatus}
|
||||
@ -202,7 +210,6 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
isSavedMessages={isSavedMessages}
|
||||
isSavedDialog={isSavedDialog}
|
||||
onEmojiStatusClick={onEmojiStatusClick}
|
||||
isUnknownUser={isUnknownUser}
|
||||
iconElement={iconElement}
|
||||
/>
|
||||
);
|
||||
@ -220,12 +227,11 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
/>
|
||||
)}
|
||||
<Avatar
|
||||
key={user?.id}
|
||||
key={customPeer?.type || user?.id}
|
||||
size={avatarSize}
|
||||
peer={user}
|
||||
peer={customPeer || user}
|
||||
className={buildClassName(isSavedDialog && 'overlay-avatar')}
|
||||
isSavedMessages={isSavedMessages}
|
||||
isUnknownUser={isUnknownUser}
|
||||
isSavedDialog={isSavedDialog}
|
||||
withStory={withStory}
|
||||
storyViewerOrigin={storyViewerOrigin}
|
||||
@ -245,11 +251,11 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { userId, forceShowSelf }): StateProps => {
|
||||
const { isSynced } = global;
|
||||
const user = selectUser(global, userId);
|
||||
const userStatus = selectUserStatus(global, userId);
|
||||
const user = userId ? selectUser(global, userId) : undefined;
|
||||
const userStatus = userId ? selectUserStatus(global, userId) : undefined;
|
||||
const isSavedMessages = !forceShowSelf && user && user.isSelf;
|
||||
const self = isSavedMessages ? user : selectUser(global, global.currentUserId!);
|
||||
const areMessagesLoaded = Boolean(selectChatMessages(global, userId));
|
||||
const areMessagesLoaded = Boolean(userId && selectChatMessages(global, userId));
|
||||
|
||||
return {
|
||||
user,
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
import type { ApiPeer, ApiPeerColor } from '../../../api/types';
|
||||
import type { CustomPeer } from '../../../types';
|
||||
|
||||
import { getPeerColorCount, getPeerColorKey } from '../../../global/helpers';
|
||||
|
||||
export function getPeerColorClass(peer?: ApiPeer, noUserColors?: boolean, shouldReset?: boolean) {
|
||||
export function getPeerColorClass(peer?: ApiPeer | CustomPeer, noUserColors?: boolean, shouldReset?: boolean) {
|
||||
if (!peer) {
|
||||
if (!shouldReset) return undefined;
|
||||
return noUserColors ? 'peer-color-count-1' : 'peer-color-0';
|
||||
}
|
||||
|
||||
if ('isCustomPeer' in peer) {
|
||||
if (!peer.peerColorId) return undefined;
|
||||
return `peer-color-${peer.peerColorId}`;
|
||||
}
|
||||
return noUserColors ? `peer-color-count-${getPeerColorCount(peer)}` : `peer-color-${getPeerColorKey(peer)}`;
|
||||
}
|
||||
|
||||
|
||||
@ -356,6 +356,7 @@ const Settings: FC<OwnProps> = ({
|
||||
return (
|
||||
<SettingsPrivacyVisibilityExceptionList
|
||||
isAllowList
|
||||
withPremiumCategory={currentScreen === SettingsScreens.PrivacyGroupChatsAllowedContacts}
|
||||
screen={currentScreen}
|
||||
onScreenSelect={onScreenSelect}
|
||||
isActive={isScreenActive || privacyAllowScreens[currentScreen]}
|
||||
|
||||
@ -103,7 +103,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
}, [updateContentSettings]);
|
||||
|
||||
function getVisibilityValue(setting?: ApiPrivacySettings) {
|
||||
const { visibility } = setting || {};
|
||||
const { visibility, shouldAllowPremium } = setting || {};
|
||||
const blockCount = setting ? setting.blockChatIds.length + setting.blockUserIds.length : 0;
|
||||
const allowCount = setting ? setting.allowChatIds.length + setting.allowUserIds.length : 0;
|
||||
const total = [];
|
||||
@ -112,6 +112,10 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const exceptionString = total.length ? `(${total.join(',')})` : '';
|
||||
|
||||
if (shouldAllowPremium) {
|
||||
return lang(exceptionString ? 'ContactsAndPremium' : 'PrivacyPremium');
|
||||
}
|
||||
|
||||
switch (visibility) {
|
||||
case 'everybody':
|
||||
return `${lang('P2PEverybody')} ${exceptionString}`;
|
||||
|
||||
@ -196,12 +196,14 @@ function PrivacySubsection({
|
||||
}
|
||||
}, [lang, screen]);
|
||||
|
||||
const prepareSubtitle = useLastCallback((userIds?: string[], chatIds?: string[]) => {
|
||||
const prepareSubtitle = useLastCallback((userIds?: string[], chatIds?: string[], shouldAllowPremium?: boolean) => {
|
||||
const userIdsCount = userIds?.length || 0;
|
||||
const chatIdsCount = chatIds?.length || 0;
|
||||
|
||||
if (!userIdsCount && !chatIdsCount) {
|
||||
return lang('EditAdminAddUsers');
|
||||
return shouldAllowPremium ? lang('PrivacyPremium') : lang('EditAdminAddUsers');
|
||||
} else if (shouldAllowPremium) {
|
||||
return lang('ContactsAndPremium');
|
||||
}
|
||||
|
||||
const userCountString = userIdsCount > 0 ? lang('Users', userIdsCount) : undefined;
|
||||
@ -211,7 +213,7 @@ function PrivacySubsection({
|
||||
});
|
||||
|
||||
const allowedString = useMemo(() => {
|
||||
return prepareSubtitle(privacy?.allowUserIds, privacy?.allowChatIds);
|
||||
return prepareSubtitle(privacy?.allowUserIds, privacy?.allowChatIds, privacy?.shouldAllowPremium);
|
||||
}, [privacy]);
|
||||
|
||||
const blockString = useMemo(() => {
|
||||
|
||||
@ -5,12 +5,15 @@ import React, {
|
||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
import type { ApiPrivacySettings } from '../../../types';
|
||||
import type { ApiPrivacySettings, CustomPeerType } from '../../../types';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID } from '../../../config';
|
||||
import { filterChatsByName } from '../../../global/helpers';
|
||||
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
||||
import {
|
||||
filterChatsByName, isChatChannel, isDeletedUser,
|
||||
} from '../../../global/helpers';
|
||||
import { unique } from '../../../util/iteratees';
|
||||
import { CUSTOM_PEER_PREMIUM } from '../../../util/objects/customPeer';
|
||||
import { getPrivacyKey } from './helpers/privacy';
|
||||
|
||||
import { useFolderManagerForOrderedIds } from '../../../hooks/useFolderManager';
|
||||
@ -22,6 +25,7 @@ import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||
|
||||
export type OwnProps = {
|
||||
isAllowList?: boolean;
|
||||
withPremiumCategory?: boolean;
|
||||
screen: SettingsScreens;
|
||||
isActive?: boolean;
|
||||
onScreenSelect: (screen: SettingsScreens) => void;
|
||||
@ -33,8 +37,11 @@ type StateProps = {
|
||||
settings?: ApiPrivacySettings;
|
||||
};
|
||||
|
||||
const PREMIUM_CATEGORY = [CUSTOM_PEER_PREMIUM];
|
||||
|
||||
const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
||||
isAllowList,
|
||||
withPremiumCategory,
|
||||
screen,
|
||||
isActive,
|
||||
currentUserId,
|
||||
@ -57,23 +64,46 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
||||
return [...settings.blockUserIds, ...settings.blockChatIds];
|
||||
}
|
||||
}, [isAllowList, settings]);
|
||||
const selectedCategoryTypes = useMemo(() => {
|
||||
if (!settings) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [settings.shouldAllowPremium ? CUSTOM_PEER_PREMIUM.type : undefined].filter(Boolean);
|
||||
}, [settings]);
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const [isSubmitShown, setIsSubmitShown] = useState<boolean>(false);
|
||||
const [newSelectedContactIds, setNewSelectedContactIds] = useState<string[]>(selectedContactIds);
|
||||
const [newSelectedCategoryTypes, setNewSelectedCategoryTypes] = useState<CustomPeerType[]>(selectedCategoryTypes);
|
||||
|
||||
// Reset selected contact ids on change from other client when screen is not active
|
||||
useEffect(() => {
|
||||
if (!isActive) setNewSelectedContactIds(selectedContactIds);
|
||||
}, [isActive, selectedContactIds]);
|
||||
if (!isActive) {
|
||||
setNewSelectedContactIds(selectedContactIds);
|
||||
setNewSelectedCategoryTypes(selectedCategoryTypes);
|
||||
}
|
||||
}, [isActive, selectedCategoryTypes, selectedContactIds]);
|
||||
|
||||
const folderAllOrderedIds = useFolderManagerForOrderedIds(ALL_FOLDER_ID);
|
||||
const folderArchivedOrderedIds = useFolderManagerForOrderedIds(ARCHIVED_FOLDER_ID);
|
||||
const displayedIds = useMemo(() => {
|
||||
// No need for expensive global updates on chats, so we avoid them
|
||||
const chatsById = getGlobal().chats.byId;
|
||||
const usersById = getGlobal().users.byId;
|
||||
|
||||
const chatIds = unique([...folderAllOrderedIds || [], ...folderArchivedOrderedIds || []])
|
||||
.filter((chatId) => chatId !== currentUserId);
|
||||
.filter((chatId) => {
|
||||
const chat = chatsById[chatId];
|
||||
const user = usersById[chatId];
|
||||
const isDeleted = user && isDeletedUser(user);
|
||||
const isChannel = chat && isChatChannel(chat);
|
||||
return chatId !== currentUserId && chatId !== SERVICE_NOTIFICATIONS_USER_ID && !isChannel && !isDeleted;
|
||||
});
|
||||
|
||||
const filteredChats = filterChatsByName(lang, chatIds, chatsById, searchQuery);
|
||||
|
||||
// Show only relevant items
|
||||
if (searchQuery) return filteredChats;
|
||||
|
||||
return unique([
|
||||
...selectedContactIds,
|
||||
@ -81,6 +111,11 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
||||
]);
|
||||
}, [folderAllOrderedIds, folderArchivedOrderedIds, selectedContactIds, lang, searchQuery, currentUserId]);
|
||||
|
||||
const handleSelectedCategoriesChange = useCallback((value: CustomPeerType[]) => {
|
||||
setNewSelectedCategoryTypes(value);
|
||||
setIsSubmitShown(true);
|
||||
}, []);
|
||||
|
||||
const handleSelectedContactIdsChange = useCallback((value: string[]) => {
|
||||
setNewSelectedContactIds(value);
|
||||
setIsSubmitShown(true);
|
||||
@ -91,10 +126,11 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
||||
privacyKey: getPrivacyKey(screen)!,
|
||||
isAllowList: Boolean(isAllowList),
|
||||
updatedIds: newSelectedContactIds,
|
||||
isPremiumAllowed: newSelectedCategoryTypes.includes(CUSTOM_PEER_PREMIUM.type) || undefined,
|
||||
});
|
||||
|
||||
onScreenSelect(SettingsScreens.Privacy);
|
||||
}, [isAllowList, newSelectedContactIds, onScreenSelect, screen, setPrivacySettings]);
|
||||
}, [isAllowList, newSelectedCategoryTypes, newSelectedContactIds, onScreenSelect, screen]);
|
||||
|
||||
useHistoryBack({
|
||||
isActive,
|
||||
@ -104,13 +140,16 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
||||
return (
|
||||
<div className="NewChat-inner step-1">
|
||||
<Picker
|
||||
categories={withPremiumCategory ? PREMIUM_CATEGORY : undefined}
|
||||
itemIds={displayedIds || []}
|
||||
selectedIds={newSelectedContactIds}
|
||||
selectedCategories={newSelectedCategoryTypes}
|
||||
filterValue={searchQuery}
|
||||
filterPlaceholder={isAllowList ? lang('AlwaysAllowPlaceholder') : lang('NeverAllowPlaceholder')}
|
||||
searchInputId="new-group-picker-search"
|
||||
isSearchable
|
||||
onSelectedIdsChange={handleSelectedContactIdsChange}
|
||||
onSelectedCategoriesChange={handleSelectedCategoriesChange}
|
||||
onFilterChange={setSearchQuery}
|
||||
/>
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ import {
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatDateAtTime } from '../../../util/date/dateFormat';
|
||||
import { CUSTOM_PEER_TO_BE_DISTRIBUTED } from '../../../util/objects/customPeer';
|
||||
import { getBoostProgressInfo } from '../../common/helpers/boostInfo';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
@ -197,12 +198,12 @@ const BoostStatistics = ({
|
||||
<PrivateChatInfo
|
||||
className={styles.user}
|
||||
userId={boost.userId}
|
||||
customPeer={!boost.userId ? CUSTOM_PEER_TO_BE_DISTRIBUTED : undefined}
|
||||
status={lang('BoostExpireOn', formatDateAtTime(lang, boost.expires * 1000))}
|
||||
noEmojiStatus
|
||||
forceShowSelf
|
||||
noFake
|
||||
noVerified
|
||||
isUnknownUser={!boost.userId}
|
||||
iconElement={boost.multiplier ? renderBoostIcon(boost.multiplier) : undefined}
|
||||
rightElement={renderBoostTypeIcon(boost)}
|
||||
/>
|
||||
|
||||
@ -19,7 +19,7 @@ type OwnProps = {
|
||||
value?: string;
|
||||
label: TeactNode;
|
||||
subLabel?: string;
|
||||
checked: boolean;
|
||||
checked?: boolean;
|
||||
rightIcon?: IconName;
|
||||
disabled?: boolean;
|
||||
tabIndex?: number;
|
||||
|
||||
@ -533,7 +533,9 @@ addActionHandler('setPrivacyVisibility', async (global, actions, payload): Promi
|
||||
});
|
||||
|
||||
addActionHandler('setPrivacySettings', async (global, actions, payload): Promise<void> => {
|
||||
const { privacyKey, isAllowList, updatedIds } = payload!;
|
||||
const {
|
||||
privacyKey, isAllowList, updatedIds, isPremiumAllowed,
|
||||
} = payload!;
|
||||
const {
|
||||
privacy: { [privacyKey]: settings },
|
||||
} = global.settings;
|
||||
@ -545,7 +547,7 @@ addActionHandler('setPrivacySettings', async (global, actions, payload): Promise
|
||||
const rules = buildApiInputPrivacyRules(global, {
|
||||
visibility: settings.visibility,
|
||||
isUnspecified: settings.isUnspecified,
|
||||
shouldAllowPremium: settings.shouldAllowPremium,
|
||||
shouldAllowPremium: isPremiumAllowed,
|
||||
allowedIds: isAllowList ? updatedIds : [...settings.allowUserIds, ...settings.allowChatIds],
|
||||
blockedIds: !isAllowList ? updatedIds : [...settings.blockUserIds, ...settings.blockChatIds],
|
||||
});
|
||||
|
||||
@ -1238,6 +1238,7 @@ export interface ActionPayloads {
|
||||
privacyKey: ApiPrivacyKey;
|
||||
isAllowList: boolean;
|
||||
updatedIds: string[];
|
||||
isPremiumAllowed?: true;
|
||||
};
|
||||
loadNotificationExceptions: undefined;
|
||||
setThemeSettings: { theme: ThemeKey } & Partial<IThemeSettings>;
|
||||
|
||||
@ -8,6 +8,7 @@ import type {
|
||||
ApiExportedInvite,
|
||||
ApiLanguage, ApiMessage, ApiReaction, ApiStickerSet, ApiUser,
|
||||
} from '../api/types';
|
||||
import type { IconName } from './icons';
|
||||
|
||||
export type TextPart = TeactNode;
|
||||
|
||||
@ -457,3 +458,16 @@ export type InlineBotSettings = {
|
||||
switchWebview?: ApiBotInlineSwitchWebview;
|
||||
cacheTime: number;
|
||||
};
|
||||
|
||||
export type CustomPeerType = 'premium' | 'toBeDistributed';
|
||||
|
||||
export interface CustomPeer {
|
||||
type: CustomPeerType;
|
||||
isCustomPeer: true;
|
||||
titleKey: string;
|
||||
subtitleKey?: string;
|
||||
avatarIcon: IconName;
|
||||
isAvatarSquare?: boolean;
|
||||
peerColorId?: number;
|
||||
withPremiumGradient?: boolean;
|
||||
}
|
||||
|
||||
19
src/util/objects/customPeer.ts
Normal file
19
src/util/objects/customPeer.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { CustomPeer } from '../../types';
|
||||
|
||||
export const CUSTOM_PEER_PREMIUM: CustomPeer = {
|
||||
isCustomPeer: true,
|
||||
type: 'premium',
|
||||
titleKey: 'PrivacyPremium',
|
||||
subtitleKey: 'PrivacyPremiumText',
|
||||
avatarIcon: 'premium',
|
||||
isAvatarSquare: true,
|
||||
withPremiumGradient: true,
|
||||
};
|
||||
|
||||
export const CUSTOM_PEER_TO_BE_DISTRIBUTED: CustomPeer = {
|
||||
isCustomPeer: true,
|
||||
type: 'toBeDistributed',
|
||||
titleKey: 'BoostingToBeDistributed',
|
||||
avatarIcon: 'user',
|
||||
withPremiumGradient: true,
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user