Support gift transfer (#5555)
This commit is contained in:
parent
1419d396d3
commit
45fc7aac7c
@ -682,6 +682,13 @@ export function buildInputInvoice(invoice: ApiRequestInputInvoice) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'stargiftTransfer': {
|
||||||
|
return new GramJs.InputInvoiceStarGiftTransfer({
|
||||||
|
stargift: buildInputSavedStarGift(invoice.inputSavedGift),
|
||||||
|
toId: buildInputPeer(invoice.recipient.id, invoice.recipient.accessHash),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'giveaway':
|
case 'giveaway':
|
||||||
default: {
|
default: {
|
||||||
const purpose = buildInputStorePaymentPurpose(invoice.purpose);
|
const purpose = buildInputStorePaymentPurpose(invoice.purpose);
|
||||||
|
|||||||
@ -620,12 +620,12 @@ async function getFullChannelInfo(
|
|||||||
? exportedInvite.link
|
? exportedInvite.link
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const { members, userStatusesById } = (canViewParticipants && await fetchMembers(id, accessHash)) || {};
|
const { members, userStatusesById } = (canViewParticipants && await fetchMembers({ chat })) || {};
|
||||||
const { members: kickedMembers, userStatusesById: bannedStatusesById } = (
|
const { members: kickedMembers, userStatusesById: bannedStatusesById } = (
|
||||||
canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'kicked')
|
canViewParticipants && adminRights && await fetchMembers({ chat, memberFilter: 'kicked' })
|
||||||
) || {};
|
) || {};
|
||||||
const { members: adminMembers, userStatusesById: adminStatusesById } = (
|
const { members: adminMembers, userStatusesById: adminStatusesById } = (
|
||||||
canViewParticipants && await fetchMembers(id, accessHash, 'admin')
|
canViewParticipants && await fetchMembers({ chat, memberFilter: 'admin' })
|
||||||
) || {};
|
) || {};
|
||||||
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
|
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
|
||||||
const memberInfoRequest = !chat.isNotJoined && chat.type === 'chatTypeChannel'
|
const memberInfoRequest = !chat.isNotJoined && chat.type === 'chatTypeChannel'
|
||||||
@ -1285,35 +1285,44 @@ export function toggleSignatures({
|
|||||||
type ChannelMembersFilter =
|
type ChannelMembersFilter =
|
||||||
'kicked'
|
'kicked'
|
||||||
| 'admin'
|
| 'admin'
|
||||||
| 'recent';
|
| 'recent'
|
||||||
|
| 'search';
|
||||||
|
|
||||||
export async function fetchMembers(
|
export async function fetchMembers({
|
||||||
chatId: string,
|
chat,
|
||||||
accessHash: string,
|
memberFilter = 'recent',
|
||||||
memberFilter: ChannelMembersFilter = 'recent',
|
offset,
|
||||||
offset?: number,
|
query = '',
|
||||||
) {
|
} : {
|
||||||
|
chat: ApiChat;
|
||||||
|
memberFilter?: ChannelMembersFilter;
|
||||||
|
offset?: number;
|
||||||
|
query?: string;
|
||||||
|
}) {
|
||||||
let filter: GramJs.TypeChannelParticipantsFilter;
|
let filter: GramJs.TypeChannelParticipantsFilter;
|
||||||
|
|
||||||
switch (memberFilter) {
|
switch (memberFilter) {
|
||||||
case 'kicked':
|
case 'kicked':
|
||||||
filter = new GramJs.ChannelParticipantsKicked({ q: '' });
|
filter = new GramJs.ChannelParticipantsKicked({ q: query });
|
||||||
break;
|
break;
|
||||||
case 'admin':
|
case 'admin':
|
||||||
filter = new GramJs.ChannelParticipantsAdmins();
|
filter = new GramJs.ChannelParticipantsAdmins();
|
||||||
break;
|
break;
|
||||||
|
case 'search':
|
||||||
|
filter = new GramJs.ChannelParticipantsSearch({ q: query });
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
filter = new GramJs.ChannelParticipantsRecent();
|
filter = new GramJs.ChannelParticipantsRecent();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await invokeRequest(new GramJs.channels.GetParticipants({
|
const result = await invokeRequest(new GramJs.channels.GetParticipants({
|
||||||
channel: buildInputEntity(chatId, accessHash) as GramJs.InputChannel,
|
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||||
filter,
|
filter,
|
||||||
offset,
|
offset,
|
||||||
limit: MEMBERS_LOAD_SLICE,
|
limit: MEMBERS_LOAD_SLICE,
|
||||||
}), {
|
}), {
|
||||||
abortControllerChatId: chatId,
|
abortControllerChatId: chat.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result || result instanceof GramJs.channels.ChannelParticipantsNotModified) {
|
if (!result || result instanceof GramJs.channels.ChannelParticipantsNotModified) {
|
||||||
|
|||||||
@ -694,7 +694,7 @@ export async function fetchStarGiftUpgradePreview({
|
|||||||
return result.sampleAttributes.map(buildApiStarGiftAttribute).filter(Boolean);
|
return result.sampleAttributes.map(buildApiStarGiftAttribute).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function upgradeGift({
|
export function upgradeStarGift({
|
||||||
inputSavedGift,
|
inputSavedGift,
|
||||||
shouldKeepOriginalDetails,
|
shouldKeepOriginalDetails,
|
||||||
}: {
|
}: {
|
||||||
@ -709,6 +709,21 @@ export function upgradeGift({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function transferStarGift({
|
||||||
|
inputSavedGift,
|
||||||
|
toPeer,
|
||||||
|
}: {
|
||||||
|
inputSavedGift: ApiRequestInputSavedStarGift;
|
||||||
|
toPeer: ApiPeer;
|
||||||
|
}) {
|
||||||
|
return invokeRequest(new GramJs.payments.TransferStarGift({
|
||||||
|
stargift: buildInputSavedStarGift(inputSavedGift),
|
||||||
|
toId: buildInputPeer(toPeer.id, toPeer.accessHash),
|
||||||
|
}), {
|
||||||
|
shouldReturnTrue: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchStarGiftWithdrawalUrl({
|
export async function fetchStarGiftWithdrawalUrl({
|
||||||
inputGift,
|
inputGift,
|
||||||
password,
|
password,
|
||||||
|
|||||||
@ -589,9 +589,16 @@ export type ApiInputInvoiceStarGiftUpgrade = {
|
|||||||
shouldKeepOriginalDetails?: true;
|
shouldKeepOriginalDetails?: true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiInputInvoiceStarGiftTransfer = {
|
||||||
|
type: 'stargiftTransfer';
|
||||||
|
inputSavedGift: ApiInputSavedStarGift;
|
||||||
|
recipientId: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway
|
export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway
|
||||||
| ApiInputInvoiceGiftCode | ApiInputInvoiceStars | ApiInputInvoiceStarsGift | ApiInputInvoiceStarGiftUpgrade
|
| ApiInputInvoiceGiftCode | ApiInputInvoiceStars | ApiInputInvoiceStarsGift
|
||||||
| ApiInputInvoiceStarsGiveaway | ApiInputInvoiceStarGift | ApiInputInvoiceChatInviteSubscription;
|
| ApiInputInvoiceStarsGiveaway | ApiInputInvoiceStarGift | ApiInputInvoiceChatInviteSubscription
|
||||||
|
| ApiInputInvoiceStarGiftUpgrade | ApiInputInvoiceStarGiftTransfer;
|
||||||
|
|
||||||
/* Used for Invoice request */
|
/* Used for Invoice request */
|
||||||
export type ApiRequestInputInvoiceMessage = {
|
export type ApiRequestInputInvoiceMessage = {
|
||||||
@ -641,6 +648,13 @@ export type ApiRequestInputInvoiceStarGiftUpgrade = {
|
|||||||
shouldKeepOriginalDetails?: true;
|
shouldKeepOriginalDetails?: true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiRequestInputInvoiceStarGiftTransfer = {
|
||||||
|
type: 'stargiftTransfer';
|
||||||
|
inputSavedGift: ApiRequestInputSavedStarGift;
|
||||||
|
recipient: ApiPeer;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug
|
export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug
|
||||||
| ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars | ApiRequestInputInvoiceStarsGiveaway
|
| ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars | ApiRequestInputInvoiceStarsGiveaway
|
||||||
| ApiRequestInputInvoiceChatInviteSubscription | ApiRequestInputInvoiceStarGift | ApiRequestInputInvoiceStarGiftUpgrade;
|
| ApiRequestInputInvoiceChatInviteSubscription | ApiRequestInputInvoiceStarGift | ApiRequestInputInvoiceStarGiftUpgrade
|
||||||
|
| ApiRequestInputInvoiceStarGiftTransfer;
|
||||||
|
|||||||
@ -1380,6 +1380,7 @@
|
|||||||
"GiftHideNameDescription" = "You can hide your name and message from visitors to {receiver}'s profile. {receiver} will still see your name and message.";
|
"GiftHideNameDescription" = "You can hide your name and message from visitors to {receiver}'s profile. {receiver} will still see your name and message.";
|
||||||
"GiftHideNameDescriptionChannel" = "You can hide your name and message from all visitors of this channel except its admins.";
|
"GiftHideNameDescriptionChannel" = "You can hide your name and message from all visitors of this channel except its admins.";
|
||||||
"GiftSend" = "Send a Gift for {amount}";
|
"GiftSend" = "Send a Gift for {amount}";
|
||||||
|
"GiftUnique" = "{title} #{number}";
|
||||||
"GiftInfoSent" = "Sent Gift";
|
"GiftInfoSent" = "Sent Gift";
|
||||||
"GiftInfoReceived" = "Received Gift";
|
"GiftInfoReceived" = "Received Gift";
|
||||||
"GiftInfoTitle" = "Gift";
|
"GiftInfoTitle" = "Gift";
|
||||||
@ -1436,7 +1437,15 @@
|
|||||||
"GiftInfoViewUpgraded" = "View Upgraded Gift";
|
"GiftInfoViewUpgraded" = "View Upgraded Gift";
|
||||||
"GiftInfoUpgradeBadge" = "upgrade";
|
"GiftInfoUpgradeBadge" = "upgrade";
|
||||||
"GiftInfoUpgradeForFree" = "Upgrade For Free";
|
"GiftInfoUpgradeForFree" = "Upgrade For Free";
|
||||||
"GiftInfoWithdraw" = "Withdraw";
|
"GiftInfoTransfer" = "Transfer";
|
||||||
|
"GiftTransferTitle" = "Transfer";
|
||||||
|
"GiftTransferTON" = "Send via Blockchain";
|
||||||
|
"GiftTransferTONBlocked" = "unlocks in {time}";
|
||||||
|
"GiftTransferConfirmDescription" = "Do you want to transfer ownership of **{gift}** to **{peer}** for **{amount}**?";
|
||||||
|
"GiftTransferConfirmDescriptionFree" = "Do you want to transfer ownership of **{gift}** to **{peer}**?";
|
||||||
|
"GiftTransferConfirmButton" = "Transfer for {amount}";
|
||||||
|
"GiftTransferConfirmButtonFree" = "Transfer";
|
||||||
|
"GiftTransferSuccessMessage" = "You have successfully gifted {gift} to {peer}.";
|
||||||
"GiftUpgradeUniqueTitle" = "Unique";
|
"GiftUpgradeUniqueTitle" = "Unique";
|
||||||
"GiftUpgradeUniqueDescription" = "Turn your gift into a unique collectible that you can transfer or auction.";
|
"GiftUpgradeUniqueDescription" = "Turn your gift into a unique collectible that you can transfer or auction.";
|
||||||
"GiftUpgradeTransferableTitle" = "Transferable";
|
"GiftUpgradeTransferableTitle" = "Transferable";
|
||||||
|
|||||||
@ -9,4 +9,5 @@ export { default as GiftModal } from '../components/modals/gift/GiftModal';
|
|||||||
export { default as GiftRecipientPicker } from '../components/modals/gift/recipient/GiftRecipientPicker';
|
export { default as GiftRecipientPicker } from '../components/modals/gift/recipient/GiftRecipientPicker';
|
||||||
export { default as GiftInfoModal } from '../components/modals/gift/info/GiftInfoModal';
|
export { default as GiftInfoModal } from '../components/modals/gift/info/GiftInfoModal';
|
||||||
export { default as GiftUpgradeModal } from '../components/modals/gift/upgrade/GiftUpgradeModal';
|
export { default as GiftUpgradeModal } from '../components/modals/gift/upgrade/GiftUpgradeModal';
|
||||||
export { default as GiftWithdrawModal } from '../components/modals/gift/fragment/GiftWithdrawModal';
|
export { default as GiftWithdrawModal } from '../components/modals/gift/withdraw/GiftWithdrawModal';
|
||||||
|
export { default as GiftTransferModal } from '../components/modals/gift/transfer/GiftTransferModal';
|
||||||
|
|||||||
@ -7,17 +7,17 @@ import type { ThreadId } from '../../types';
|
|||||||
|
|
||||||
import { API_CHAT_TYPES } from '../../config';
|
import { API_CHAT_TYPES } from '../../config';
|
||||||
import {
|
import {
|
||||||
filterChatsByName,
|
|
||||||
filterUsersByName,
|
|
||||||
getCanPostInChat,
|
getCanPostInChat,
|
||||||
isDeletedUser,
|
isDeletedUser,
|
||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
import { filterChatIdsByType } from '../../global/selectors';
|
import { filterPeersByQuery } from '../../global/helpers/peers';
|
||||||
|
import {
|
||||||
|
filterChatIdsByType, selectChat, selectChatFullInfo, selectUser,
|
||||||
|
} from '../../global/selectors';
|
||||||
import { unique } from '../../util/iteratees';
|
import { unique } from '../../util/iteratees';
|
||||||
import sortChatIds from './helpers/sortChatIds';
|
import sortChatIds from './helpers/sortChatIds';
|
||||||
|
|
||||||
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
|
||||||
|
|
||||||
import ChatOrUserPicker from './pickers/ChatOrUserPicker';
|
import ChatOrUserPicker from './pickers/ChatOrUserPicker';
|
||||||
|
|
||||||
@ -55,7 +55,6 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
onCloseAnimationEnd,
|
onCloseAnimationEnd,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useOldLang();
|
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const ids = useMemo(() => {
|
const ids = useMemo(() => {
|
||||||
if (!isOpen) return undefined;
|
if (!isOpen) return undefined;
|
||||||
@ -67,34 +66,36 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
// No need for expensive global updates on users, so we avoid them
|
// No need for expensive global updates on users, so we avoid them
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
const usersById = global.users.byId;
|
|
||||||
const chatsById = global.chats.byId;
|
|
||||||
const chatFullInfoById = global.chats.fullInfoById;
|
|
||||||
|
|
||||||
const chatIds = [
|
const peerIds = [
|
||||||
...(activeListIds || []),
|
...(activeListIds || []),
|
||||||
...((search && archivedListIds) || []),
|
...((search && archivedListIds) || []),
|
||||||
].filter((id) => {
|
].filter((id) => {
|
||||||
const chat = chatsById[id];
|
const chat = selectChat(global, id);
|
||||||
const user = usersById[id];
|
const user = selectUser(global, id);
|
||||||
if (user && isDeletedUser(user)) return false;
|
if (user && isDeletedUser(user)) return false;
|
||||||
|
|
||||||
return chat && getCanPostInChat(chat, undefined, undefined, chatFullInfoById[id]);
|
const chatFullInfo = selectChatFullInfo(global, id);
|
||||||
|
|
||||||
|
return chat && chatFullInfo && getCanPostInChat(chat, undefined, undefined, chatFullInfo);
|
||||||
});
|
});
|
||||||
|
|
||||||
const sorted = sortChatIds(
|
const sorted = sortChatIds(
|
||||||
unique([
|
filterPeersByQuery({
|
||||||
...(currentUserId ? [currentUserId] : []),
|
ids: unique([
|
||||||
...filterChatsByName(lang, chatIds, chatsById, search, currentUserId),
|
...(currentUserId ? [currentUserId] : []),
|
||||||
...(contactIds && filter.includes('users') ? filterUsersByName(contactIds, usersById, search) : []),
|
...peerIds,
|
||||||
]),
|
...(contactIds || []),
|
||||||
|
]),
|
||||||
|
query: search,
|
||||||
|
}),
|
||||||
undefined,
|
undefined,
|
||||||
priorityIds,
|
priorityIds,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
);
|
);
|
||||||
|
|
||||||
return filterChatIdsByType(global, sorted, filter);
|
return filterChatIdsByType(global, sorted, filter);
|
||||||
}, [pinnedIds, currentUserId, activeListIds, search, archivedListIds, lang, contactIds, filter, isOpen]);
|
}, [pinnedIds, currentUserId, activeListIds, search, archivedListIds, contactIds, filter, isOpen]);
|
||||||
|
|
||||||
const renderingIds = useCurrentOrPrev(ids, true)!;
|
const renderingIds = useCurrentOrPrev(ids, true)!;
|
||||||
|
|
||||||
|
|||||||
@ -31,18 +31,18 @@ import PickerItem from './PickerItem';
|
|||||||
|
|
||||||
import styles from './PickerStyles.module.scss';
|
import styles from './PickerStyles.module.scss';
|
||||||
|
|
||||||
type SingleModeProps = {
|
type SingleModeProps<CategoryType extends string> = {
|
||||||
allowMultiple?: false;
|
allowMultiple?: false;
|
||||||
itemInputType?: 'radio';
|
itemInputType?: 'radio';
|
||||||
selectedId?: string;
|
selectedId?: string;
|
||||||
selectedIds?: never; // Help TS to throw an error if this is passed
|
selectedIds?: never; // Help TS to throw an error if this is passed
|
||||||
selectedCategory?: CustomPeerType;
|
selectedCategory?: CategoryType;
|
||||||
selectedCategories?: never;
|
selectedCategories?: never;
|
||||||
onSelectedCategoryChange?: (category: CustomPeerType) => void;
|
onSelectedCategoryChange?: (category: CategoryType) => void;
|
||||||
onSelectedIdChange?: (id: string) => void;
|
onSelectedIdChange?: (id: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MultipleModeProps = {
|
type MultipleModeProps<CategoryType extends string> = {
|
||||||
allowMultiple: true;
|
allowMultiple: true;
|
||||||
itemInputType: 'checkbox';
|
itemInputType: 'checkbox';
|
||||||
selectedId?: never;
|
selectedId?: never;
|
||||||
@ -50,14 +50,14 @@ type MultipleModeProps = {
|
|||||||
lockedSelectedIds?: string[];
|
lockedSelectedIds?: string[];
|
||||||
lockedUnselectedIds?: string[];
|
lockedUnselectedIds?: string[];
|
||||||
selectedCategory?: never;
|
selectedCategory?: never;
|
||||||
selectedCategories?: CustomPeerType[];
|
selectedCategories?: CategoryType[];
|
||||||
onSelectedCategoriesChange?: (categories: CustomPeerType[]) => void;
|
onSelectedCategoriesChange?: (categories: CategoryType[]) => void;
|
||||||
onSelectedIdsChange?: (Ids: string[]) => void;
|
onSelectedIdsChange?: (Ids: string[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps<CategoryType extends string> = {
|
||||||
className?: string;
|
className?: string;
|
||||||
categories?: UniqueCustomPeer[];
|
categories?: UniqueCustomPeer<CategoryType>[];
|
||||||
itemIds: string[];
|
itemIds: string[];
|
||||||
lockedUnselectedSubtitle?: string;
|
lockedUnselectedSubtitle?: string;
|
||||||
filterValue?: string;
|
filterValue?: string;
|
||||||
@ -73,11 +73,12 @@ type OwnProps = {
|
|||||||
isViewOnly?: boolean;
|
isViewOnly?: boolean;
|
||||||
withStatus?: boolean;
|
withStatus?: boolean;
|
||||||
withPeerTypes?: boolean;
|
withPeerTypes?: boolean;
|
||||||
|
withPeerUsernames?: boolean;
|
||||||
withDefaultPadding?: boolean;
|
withDefaultPadding?: boolean;
|
||||||
onFilterChange?: (value: string) => void;
|
onFilterChange?: (value: string) => void;
|
||||||
onDisabledClick?: (id: string, isSelected: boolean) => void;
|
onDisabledClick?: (id: string, isSelected: boolean) => void;
|
||||||
onLoadMore?: () => void;
|
onLoadMore?: () => void;
|
||||||
} & (SingleModeProps | MultipleModeProps);
|
} & (SingleModeProps<CategoryType> | MultipleModeProps<CategoryType>);
|
||||||
|
|
||||||
// Focus slows down animation, also it breaks transition layout in Chrome
|
// Focus slows down animation, also it breaks transition layout in Chrome
|
||||||
const FOCUS_DELAY_MS = 500;
|
const FOCUS_DELAY_MS = 500;
|
||||||
@ -87,7 +88,7 @@ const ALWAYS_FULL_ITEMS_COUNT = 5;
|
|||||||
|
|
||||||
const ITEM_CLASS_NAME = 'PeerPickerItem';
|
const ITEM_CLASS_NAME = 'PeerPickerItem';
|
||||||
|
|
||||||
const PeerPicker = ({
|
const PeerPicker = <CategoryType extends string = CustomPeerType>({
|
||||||
className,
|
className,
|
||||||
categories,
|
categories,
|
||||||
itemIds,
|
itemIds,
|
||||||
@ -106,12 +107,13 @@ const PeerPicker = ({
|
|||||||
itemInputType,
|
itemInputType,
|
||||||
withStatus,
|
withStatus,
|
||||||
withPeerTypes,
|
withPeerTypes,
|
||||||
|
withPeerUsernames,
|
||||||
withDefaultPadding,
|
withDefaultPadding,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
onDisabledClick,
|
onDisabledClick,
|
||||||
onLoadMore,
|
onLoadMore,
|
||||||
...optionalProps
|
...optionalProps
|
||||||
}: OwnProps) => {
|
}: OwnProps<CategoryType>) => {
|
||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
|
|
||||||
const allowMultiple = optionalProps.allowMultiple;
|
const allowMultiple = optionalProps.allowMultiple;
|
||||||
@ -268,7 +270,16 @@ const PeerPicker = ({
|
|||||||
|
|
||||||
function getSubtitle() {
|
function getSubtitle() {
|
||||||
if (isAlwaysUnselected) return [lockedUnselectedSubtitle];
|
if (isAlwaysUnselected) return [lockedUnselectedSubtitle];
|
||||||
if (withStatus && peer) {
|
if (!peer) return undefined;
|
||||||
|
|
||||||
|
if (withPeerUsernames) {
|
||||||
|
const username = peer.usernames?.[0]?.username;
|
||||||
|
if (username) {
|
||||||
|
return [`@${username}`];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (withStatus) {
|
||||||
if (isApiPeerChat(peer)) {
|
if (isApiPeerChat(peer)) {
|
||||||
return [getGroupStatus(lang, peer)];
|
return [getGroupStatus(lang, peer)];
|
||||||
}
|
}
|
||||||
@ -279,10 +290,12 @@ const PeerPicker = ({
|
|||||||
buildClassName(isUserOnline(peer, userStatus, true) && styles.onlineStatus),
|
buildClassName(isUserOnline(peer, userStatus, true) && styles.onlineStatus),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (withPeerTypes && peer) {
|
|
||||||
|
if (withPeerTypes) {
|
||||||
const langKey = getPeerTypeKey(peer);
|
const langKey = getPeerTypeKey(peer);
|
||||||
return langKey && [lang(langKey)];
|
return langKey && [lang(langKey)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +329,7 @@ const PeerPicker = ({
|
|||||||
}, [
|
}, [
|
||||||
categoriesByType, forceShowSelf, isViewOnly, itemClassName, itemInputType, lang, lockedSelectedIdsSet,
|
categoriesByType, forceShowSelf, isViewOnly, itemClassName, itemInputType, lang, lockedSelectedIdsSet,
|
||||||
lockedUnselectedIdsSet, lockedUnselectedSubtitle, onDisabledClick, selectedCategories, selectedIds,
|
lockedUnselectedIdsSet, lockedUnselectedSubtitle, onDisabledClick, selectedCategories, selectedIds,
|
||||||
withPeerTypes, withStatus,
|
withPeerTypes, withStatus, withPeerUsernames,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const beforeChildren = useMemo(() => {
|
const beforeChildren = useMemo(() => {
|
||||||
|
|||||||
@ -34,11 +34,12 @@
|
|||||||
.pickerCategoryTitle {
|
.pickerCategoryTitle {
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
padding-inline: 0.5rem;
|
padding-inline: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
|
|
||||||
&:not(:first-child) {
|
&:not(:first-child) {
|
||||||
border-top: 1px solid var(--color-borders);
|
border-top: 1px solid var(--color-borders);
|
||||||
padding-top: 0.75rem;
|
padding-top: 0.5rem;
|
||||||
margin-top: 0.375rem;
|
margin-top: 0.375rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,8 @@ import { getActions, withGlobal } from '../../../global';
|
|||||||
import type { ApiUser, ApiUserStatus } from '../../../api/types';
|
import type { ApiUser, ApiUserStatus } from '../../../api/types';
|
||||||
import { StoryViewerOrigin } from '../../../types';
|
import { StoryViewerOrigin } from '../../../types';
|
||||||
|
|
||||||
import { filterUsersByName, sortUserIds } from '../../../global/helpers';
|
import { sortUserIds } from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
|
|
||||||
import useAppLayout from '../../../hooks/useAppLayout';
|
import useAppLayout from '../../../hooks/useAppLayout';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
@ -61,7 +62,7 @@ const ContactList: FC<OwnProps & StateProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredIds = filterUsersByName(contactIds, usersById, filter);
|
const filteredIds = filterPeersByQuery({ ids: contactIds, query: filter, type: 'user' });
|
||||||
|
|
||||||
return sortUserIds(filteredIds, usersById, userStatusesById);
|
return sortUserIds(filteredIds, usersById, userStatusesById);
|
||||||
}, [contactIds, filter, usersById, userStatusesById]);
|
}, [contactIds, filter, usersById, userStatusesById]);
|
||||||
|
|||||||
@ -2,7 +2,8 @@ import type { FC } from '../../../lib/teact/teact';
|
|||||||
import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact';
|
import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { filterUsersByName, isUserBot } from '../../../global/helpers';
|
import { isUserBot } from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { selectTabState } from '../../../global/selectors';
|
import { selectTabState } from '../../../global/selectors';
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
import sortChatIds from '../../common/helpers/sortChatIds';
|
import sortChatIds from '../../common/helpers/sortChatIds';
|
||||||
@ -63,7 +64,8 @@ const NewChatStep1: FC<OwnProps & StateProps> = ({
|
|||||||
const displayedIds = useMemo(() => {
|
const displayedIds = useMemo(() => {
|
||||||
// No need for expensive global updates on users, so we avoid them
|
// No need for expensive global updates on users, so we avoid them
|
||||||
const usersById = getGlobal().users.byId;
|
const usersById = getGlobal().users.byId;
|
||||||
const foundContactIds = localContactIds ? filterUsersByName(localContactIds, usersById, searchQuery) : [];
|
const foundContactIds = localContactIds
|
||||||
|
? filterPeersByQuery({ ids: localContactIds, query: searchQuery, type: 'user' }) : [];
|
||||||
|
|
||||||
return sortChatIds(
|
return sortChatIds(
|
||||||
unique([
|
unique([
|
||||||
|
|||||||
@ -3,12 +3,12 @@ import React, {
|
|||||||
memo, useCallback, useMemo, useRef,
|
memo, useCallback, useMemo, useRef,
|
||||||
useState,
|
useState,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { LoadMoreDirection } from '../../../types';
|
import { LoadMoreDirection } from '../../../types';
|
||||||
|
|
||||||
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
||||||
import { filterUsersByName } from '../../../global/helpers';
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { selectTabState } from '../../../global/selectors';
|
import { selectTabState } from '../../../global/selectors';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
|
|
||||||
@ -58,8 +58,7 @@ const BotAppResults: FC<OwnProps & StateProps> = ({
|
|||||||
const recentSet = new Set(recentBotIds);
|
const recentSet = new Set(recentBotIds);
|
||||||
const withoutRecent = foundIds.filter((id) => !recentSet.has(id));
|
const withoutRecent = foundIds.filter((id) => !recentSet.has(id));
|
||||||
|
|
||||||
const usersById = getGlobal().users.byId;
|
return filterPeersByQuery({ ids: withoutRecent, query: searchQuery, type: 'user' });
|
||||||
return filterUsersByName(withoutRecent, usersById, searchQuery);
|
|
||||||
}, [foundIds, recentBotIds, searchQuery]);
|
}, [foundIds, recentBotIds, searchQuery]);
|
||||||
|
|
||||||
const handleChatClick = useLastCallback((id: string) => {
|
const handleChatClick = useLastCallback((id: string) => {
|
||||||
|
|||||||
@ -10,10 +10,9 @@ import { LoadMoreDirection } from '../../../types';
|
|||||||
|
|
||||||
import { ALL_FOLDER_ID, GLOBAL_SUGGESTED_CHANNELS_ID } from '../../../config';
|
import { ALL_FOLDER_ID, GLOBAL_SUGGESTED_CHANNELS_ID } from '../../../config';
|
||||||
import {
|
import {
|
||||||
filterChatsByName,
|
|
||||||
filterUsersByName,
|
|
||||||
isChatChannel,
|
isChatChannel,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { selectSimilarChannelIds, selectTabState } from '../../../global/selectors';
|
import { selectSimilarChannelIds, selectTabState } from '../../../global/selectors';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { getOrderedIds } from '../../../util/folderManager';
|
import { getOrderedIds } from '../../../util/folderManager';
|
||||||
@ -221,7 +220,6 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No need for expensive global updates, so we avoid them
|
// No need for expensive global updates, so we avoid them
|
||||||
const usersById = getGlobal().users.byId;
|
|
||||||
const chatsById = getGlobal().chats.byId;
|
const chatsById = getGlobal().chats.byId;
|
||||||
|
|
||||||
const orderedChatIds = getOrderedIds(ALL_FOLDER_ID) ?? [];
|
const orderedChatIds = getOrderedIds(ALL_FOLDER_ID) ?? [];
|
||||||
@ -230,7 +228,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
const chat = chatsById[id];
|
const chat = chatsById[id];
|
||||||
return chat && isChatChannel(chat);
|
return chat && isChatChannel(chat);
|
||||||
});
|
});
|
||||||
const localChatIds = filterChatsByName(oldLang, filteredChatIds, chatsById, searchQuery, currentUserId);
|
const localChatIds = filterPeersByQuery({ ids: filteredChatIds, query: searchQuery, type: 'chat' });
|
||||||
|
|
||||||
if (isChannelList) return localChatIds;
|
if (isChannelList) return localChatIds;
|
||||||
|
|
||||||
@ -239,9 +237,9 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
...(contactIds || []),
|
...(contactIds || []),
|
||||||
];
|
];
|
||||||
|
|
||||||
const localContactIds = filterUsersByName(
|
const localContactIds = filterPeersByQuery({
|
||||||
contactIdsWithMe, usersById, searchQuery, currentUserId, oldLang('SavedMessages'),
|
ids: contactIdsWithMe, query: searchQuery, type: 'user',
|
||||||
);
|
});
|
||||||
|
|
||||||
const localPeerIds = [
|
const localPeerIds = [
|
||||||
...localContactIds,
|
...localContactIds,
|
||||||
@ -252,7 +250,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
|
|||||||
...sortChatIds(localPeerIds, undefined, currentUserId ? [currentUserId] : undefined),
|
...sortChatIds(localPeerIds, undefined, currentUserId ? [currentUserId] : undefined),
|
||||||
...sortChatIds(accountPeerIds || []),
|
...sortChatIds(accountPeerIds || []),
|
||||||
]);
|
]);
|
||||||
}, [searchQuery, oldLang, currentUserId, contactIds, accountPeerIds, isChannelList]);
|
}, [searchQuery, currentUserId, contactIds, accountPeerIds, isChannelList]);
|
||||||
|
|
||||||
useHorizontalScroll(chatSelectionRef, !localResults.length || isChannelList, true);
|
useHorizontalScroll(chatSelectionRef, !localResults.length || isChannelList, true);
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,8 @@ import { getActions, withGlobal } from '../../../global';
|
|||||||
|
|
||||||
import type { ApiUser } from '../../../api/types';
|
import type { ApiUser } from '../../../api/types';
|
||||||
|
|
||||||
import { filterUsersByName, getUserFullName } from '../../../global/helpers';
|
import { getUserFullName } from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { selectTabState } from '../../../global/selectors';
|
import { selectTabState } from '../../../global/selectors';
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ const BlockUserModal: FC<OwnProps & StateProps> = ({
|
|||||||
return contactId !== currentUserId && !blockedIds.includes(contactId);
|
return contactId !== currentUserId && !blockedIds.includes(contactId);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return filterUsersByName(availableContactIds, usersById, search)
|
return filterPeersByQuery({ ids: availableContactIds, query: search, type: 'user' })
|
||||||
.sort((firstId, secondId) => {
|
.sort((firstId, secondId) => {
|
||||||
const firstName = getUserFullName(usersById[firstId]) || '';
|
const firstName = getUserFullName(usersById[firstId]) || '';
|
||||||
const secondName = getUserFullName(usersById[secondId]) || '';
|
const secondName = getUserFullName(usersById[secondId]) || '';
|
||||||
|
|||||||
@ -11,8 +11,9 @@ import { SettingsScreens } from '../../../types';
|
|||||||
|
|
||||||
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
||||||
import {
|
import {
|
||||||
filterChatsByName, isChatChannel, isDeletedUser,
|
isChatChannel, isDeletedUser,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
import { CUSTOM_PEER_PREMIUM } from '../../../util/objects/customPeer';
|
import { CUSTOM_PEER_PREMIUM } from '../../../util/objects/customPeer';
|
||||||
import { getPrivacyKey } from './helpers/privacy';
|
import { getPrivacyKey } from './helpers/privacy';
|
||||||
@ -122,16 +123,16 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
|||||||
return chatId !== currentUserId && chatId !== SERVICE_NOTIFICATIONS_USER_ID && !isChannel && !isDeleted;
|
return chatId !== currentUserId && chatId !== SERVICE_NOTIFICATIONS_USER_ID && !isChannel && !isDeleted;
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredChats = filterChatsByName(oldLang, chatIds, chatsById, searchQuery);
|
const filteredChats = filterPeersByQuery({ ids: chatIds, query: searchQuery });
|
||||||
|
|
||||||
// Show only relevant items
|
// Show only relevant items
|
||||||
if (searchQuery) return filteredChats;
|
if (searchQuery) return filteredChats;
|
||||||
|
|
||||||
return unique([
|
return unique([
|
||||||
...selectedContactIds,
|
...selectedContactIds,
|
||||||
...filterChatsByName(oldLang, chatIds, chatsById, searchQuery),
|
...chatIds,
|
||||||
]);
|
]);
|
||||||
}, [folderAllOrderedIds, folderArchivedOrderedIds, selectedContactIds, oldLang, searchQuery, currentUserId]);
|
}, [folderAllOrderedIds, folderArchivedOrderedIds, selectedContactIds, searchQuery, currentUserId]);
|
||||||
|
|
||||||
const handleSelectedCategoriesChange = useCallback((value: CustomPeerType[]) => {
|
const handleSelectedCategoriesChange = useCallback((value: CustomPeerType[]) => {
|
||||||
setNewSelectedCategoryTypes(value);
|
setNewSelectedCategoryTypes(value);
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import type { FC } from '../../../../lib/teact/teact';
|
|||||||
import React, {
|
import React, {
|
||||||
memo, useEffect, useMemo, useState,
|
memo, useEffect, useMemo, useState,
|
||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
import { getActions, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import type { FolderEditDispatch, FoldersState } from '../../../../hooks/reducers/useFoldersReducer';
|
import type { FolderEditDispatch, FoldersState } from '../../../../hooks/reducers/useFoldersReducer';
|
||||||
|
|
||||||
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID } from '../../../../config';
|
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID } from '../../../../config';
|
||||||
import { filterChatsByName } from '../../../../global/helpers';
|
import { filterPeersByQuery } from '../../../../global/helpers/peers';
|
||||||
import { selectCurrentLimit } from '../../../../global/selectors/limits';
|
import { selectCurrentLimit } from '../../../../global/selectors/limits';
|
||||||
import { unique } from '../../../../util/iteratees';
|
import { unique } from '../../../../util/iteratees';
|
||||||
import { CUSTOM_PEER_EXCLUDED_CHAT_TYPES, CUSTOM_PEER_INCLUDED_CHAT_TYPES } from '../../../../util/objects/customPeer';
|
import { CUSTOM_PEER_EXCLUDED_CHAT_TYPES, CUSTOM_PEER_INCLUDED_CHAT_TYPES } from '../../../../util/objects/customPeer';
|
||||||
@ -67,14 +67,11 @@ const SettingsFoldersChatFilters: FC<OwnProps & StateProps> = ({
|
|||||||
}, [isActive]);
|
}, [isActive]);
|
||||||
|
|
||||||
const displayedIds = useMemo(() => {
|
const displayedIds = useMemo(() => {
|
||||||
// No need for expensive global updates on chats, so we avoid them
|
|
||||||
const chatsById = getGlobal().chats.byId;
|
|
||||||
|
|
||||||
const chatIds = [...folderAllOrderedIds || [], ...folderArchivedOrderedIds || []];
|
const chatIds = [...folderAllOrderedIds || [], ...folderArchivedOrderedIds || []];
|
||||||
return unique([
|
return unique([
|
||||||
...filterChatsByName(lang, chatIds, chatsById, chatFilter),
|
...filterPeersByQuery({ ids: chatIds, query: chatFilter, type: 'chat' }),
|
||||||
]);
|
]);
|
||||||
}, [folderAllOrderedIds, folderArchivedOrderedIds, lang, chatFilter]);
|
}, [folderAllOrderedIds, folderArchivedOrderedIds, chatFilter]);
|
||||||
|
|
||||||
const handleFilterChange = useLastCallback((newFilter: string) => {
|
const handleFilterChange = useLastCallback((newFilter: string) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import React, {
|
|||||||
import { getActions, getGlobal } from '../../../global';
|
import { getActions, getGlobal } from '../../../global';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
filterChatsByName, isChatChannel, isChatPublic, isChatSuperGroup,
|
isChatChannel, isChatPublic, isChatSuperGroup,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
import sortChatIds from '../../common/helpers/sortChatIds';
|
import sortChatIds from '../../common/helpers/sortChatIds';
|
||||||
|
|
||||||
@ -61,13 +62,12 @@ const GiveawayChannelPickerModal = ({
|
|||||||
}, [giveawayChatId]);
|
}, [giveawayChatId]);
|
||||||
|
|
||||||
const displayedChannelIds = useMemo(() => {
|
const displayedChannelIds = useMemo(() => {
|
||||||
const chatsById = getGlobal().chats.byId;
|
const foundChannelIds = channelIds ? filterPeersByQuery({ ids: channelIds, query: searchQuery, type: 'chat' }) : [];
|
||||||
const foundChannelIds = channelIds ? filterChatsByName(lang, channelIds, chatsById, searchQuery) : [];
|
|
||||||
|
|
||||||
return sortChatIds(foundChannelIds,
|
return sortChatIds(foundChannelIds,
|
||||||
false,
|
false,
|
||||||
selectedIds);
|
selectedIds);
|
||||||
}, [channelIds, lang, searchQuery, selectedIds]);
|
}, [channelIds, searchQuery, selectedIds]);
|
||||||
|
|
||||||
const handleSelectedChannelIdsChange = useLastCallback((newSelectedIds: string[]) => {
|
const handleSelectedChannelIdsChange = useLastCallback((newSelectedIds: string[]) => {
|
||||||
const chatsById = getGlobal().chats.byId;
|
const chatsById = getGlobal().chats.byId;
|
||||||
|
|||||||
@ -6,10 +6,10 @@ import { getActions, getGlobal, withGlobal } from '../../../global';
|
|||||||
import type { ApiChatMember } from '../../../api/types';
|
import type { ApiChatMember } from '../../../api/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
filterUsersByName,
|
|
||||||
isUserBot,
|
isUserBot,
|
||||||
sortUserIds,
|
sortUserIds,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { selectChatFullInfo } from '../../../global/selectors';
|
import { selectChatFullInfo } from '../../../global/selectors';
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
import sortChatIds from '../../common/helpers/sortChatIds';
|
import sortChatIds from '../../common/helpers/sortChatIds';
|
||||||
@ -74,9 +74,10 @@ const GiveawayUserPickerModal = ({
|
|||||||
|
|
||||||
const displayedMemberIds = useMemo(() => {
|
const displayedMemberIds = useMemo(() => {
|
||||||
const usersById = getGlobal().users.byId;
|
const usersById = getGlobal().users.byId;
|
||||||
const filteredContactIds = memberIds ? filterUsersByName(memberIds, usersById, searchQuery) : [];
|
const filteredUserIds = memberIds
|
||||||
|
? filterPeersByQuery({ ids: memberIds, query: searchQuery, type: 'user' }) : [];
|
||||||
|
|
||||||
return sortChatIds(unique(filteredContactIds).filter((userId) => {
|
return sortChatIds(unique(filteredUserIds).filter((userId) => {
|
||||||
const user = usersById[userId];
|
const user = usersById[userId];
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -7,8 +7,9 @@ import { getActions, getGlobal, withGlobal } from '../../../global';
|
|||||||
|
|
||||||
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
||||||
import {
|
import {
|
||||||
filterUsersByName, isDeletedUser, isUserBot,
|
isDeletedUser, isUserBot,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
import sortChatIds from '../../common/helpers/sortChatIds';
|
import sortChatIds from '../../common/helpers/sortChatIds';
|
||||||
|
|
||||||
@ -46,15 +47,17 @@ const StarsGiftingPickerModal: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const displayedUserIds = useMemo(() => {
|
const displayedUserIds = useMemo(() => {
|
||||||
const usersById = getGlobal().users.byId;
|
const usersById = getGlobal().users.byId;
|
||||||
const combinedIds = [
|
const combinedIds = unique([
|
||||||
...(userIds || []),
|
...(userIds || []),
|
||||||
...(activeListIds || []),
|
...(activeListIds || []),
|
||||||
...(archivedListIds || []),
|
...(archivedListIds || []),
|
||||||
];
|
]);
|
||||||
|
|
||||||
const filteredContactIds = filterUsersByName(combinedIds, usersById, searchQuery);
|
const filteredUserIds = filterPeersByQuery({
|
||||||
|
ids: combinedIds, query: searchQuery, type: 'user',
|
||||||
|
});
|
||||||
|
|
||||||
return sortChatIds(unique(filteredContactIds).filter((id) => {
|
return sortChatIds(filteredUserIds.filter((id) => {
|
||||||
const user = usersById[id];
|
const user = usersById[id];
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|||||||
@ -7,7 +7,8 @@ import type { Signal } from '../../../../util/signals';
|
|||||||
import { ApiMessageEntityTypes } from '../../../../api/types';
|
import { ApiMessageEntityTypes } from '../../../../api/types';
|
||||||
|
|
||||||
import { requestNextMutation } from '../../../../lib/fasterdom/fasterdom';
|
import { requestNextMutation } from '../../../../lib/fasterdom/fasterdom';
|
||||||
import { filterUsersByName, getMainUsername, getUserFirstOrLastName } from '../../../../global/helpers';
|
import { getMainUsername, getUserFirstOrLastName } from '../../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../../global/helpers/peers';
|
||||||
import focusEditableElement from '../../../../util/focusEditableElement';
|
import focusEditableElement from '../../../../util/focusEditableElement';
|
||||||
import { pickTruthy, unique } from '../../../../util/iteratees';
|
import { pickTruthy, unique } from '../../../../util/iteratees';
|
||||||
import { getCaretPosition, getHtmlBeforeSelection, setCaretPosition } from '../../../../util/selection';
|
import { getCaretPosition, getHtmlBeforeSelection, setCaretPosition } from '../../../../util/selection';
|
||||||
@ -82,10 +83,14 @@ export default function useMentionTooltip(
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const filter = usernameTag.substring(1);
|
const filter = usernameTag.substring(1);
|
||||||
const filteredIds = filterUsersByName(unique([
|
const filteredIds = filterPeersByQuery({
|
||||||
...((getWithInlineBots() && topInlineBotIds) || []),
|
ids: unique([
|
||||||
...(memberIds || []),
|
...((getWithInlineBots() && topInlineBotIds) || []),
|
||||||
]), usersById, filter);
|
...(memberIds || []),
|
||||||
|
]),
|
||||||
|
query: filter,
|
||||||
|
type: 'user',
|
||||||
|
});
|
||||||
|
|
||||||
setFilteredUsers(Object.values(pickTruthy(usersById, filteredIds)));
|
setFilteredUsers(Object.values(pickTruthy(usersById, filteredIds)));
|
||||||
}, [currentUserId, groupChatMembers, topInlineBotIds, getUsernameTag, getWithInlineBots]);
|
}, [currentUserId, groupChatMembers, topInlineBotIds, getUsernameTag, getWithInlineBots]);
|
||||||
|
|||||||
@ -15,11 +15,12 @@ import ChatInviteModal from './chatInvite/ChatInviteModal.async';
|
|||||||
import ChatlistModal from './chatlist/ChatlistModal.async';
|
import ChatlistModal from './chatlist/ChatlistModal.async';
|
||||||
import CollectibleInfoModal from './collectible/CollectibleInfoModal.async';
|
import CollectibleInfoModal from './collectible/CollectibleInfoModal.async';
|
||||||
import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.async';
|
import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.async';
|
||||||
import GiftWithdrawModal from './gift/fragment/GiftWithdrawModal.async';
|
|
||||||
import PremiumGiftModal from './gift/GiftModal.async';
|
import PremiumGiftModal from './gift/GiftModal.async';
|
||||||
import GiftInfoModal from './gift/info/GiftInfoModal.async';
|
import GiftInfoModal from './gift/info/GiftInfoModal.async';
|
||||||
import GiftRecipientPicker from './gift/recipient/GiftRecipientPicker.async';
|
import GiftRecipientPicker from './gift/recipient/GiftRecipientPicker.async';
|
||||||
|
import GiftTransferModal from './gift/transfer/GiftTransferModal.async';
|
||||||
import GiftUpgradeModal from './gift/upgrade/GiftUpgradeModal.async';
|
import GiftUpgradeModal from './gift/upgrade/GiftUpgradeModal.async';
|
||||||
|
import GiftWithdrawModal from './gift/withdraw/GiftWithdrawModal.async';
|
||||||
import GiftCodeModal from './giftcode/GiftCodeModal.async';
|
import GiftCodeModal from './giftcode/GiftCodeModal.async';
|
||||||
import InviteViaLinkModal from './inviteViaLink/InviteViaLinkModal.async';
|
import InviteViaLinkModal from './inviteViaLink/InviteViaLinkModal.async';
|
||||||
import LocationAccessModal from './locationAccess/LocationAccessModal.async';
|
import LocationAccessModal from './locationAccess/LocationAccessModal.async';
|
||||||
@ -69,7 +70,8 @@ type ModalKey = keyof Pick<TabState,
|
|||||||
'aboutAdsModal' |
|
'aboutAdsModal' |
|
||||||
'giftUpgradeModal' |
|
'giftUpgradeModal' |
|
||||||
'monetizationVerificationModal' |
|
'monetizationVerificationModal' |
|
||||||
'giftWithdrawModal'
|
'giftWithdrawModal' |
|
||||||
|
'giftTransferModal'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -115,6 +117,7 @@ const MODALS: ModalRegistry = {
|
|||||||
giftUpgradeModal: GiftUpgradeModal,
|
giftUpgradeModal: GiftUpgradeModal,
|
||||||
monetizationVerificationModal: VerificationMonetizationModal,
|
monetizationVerificationModal: VerificationMonetizationModal,
|
||||||
giftWithdrawModal: GiftWithdrawModal,
|
giftWithdrawModal: GiftWithdrawModal,
|
||||||
|
giftTransferModal: GiftTransferModal,
|
||||||
};
|
};
|
||||||
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
||||||
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const GiftInfoModal = ({
|
|||||||
openGiftUpgradeModal,
|
openGiftUpgradeModal,
|
||||||
showNotification,
|
showNotification,
|
||||||
openChatWithDraft,
|
openChatWithDraft,
|
||||||
openGiftWithdrawModal,
|
openGiftTransferModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const [isConvertConfirmOpen, openConvertConfirm, closeConvertConfirm] = useFlag();
|
const [isConvertConfirmOpen, openConvertConfirm, closeConvertConfirm] = useFlag();
|
||||||
@ -128,9 +128,9 @@ const GiftInfoModal = ({
|
|||||||
handleClose();
|
handleClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleWithdraw = useLastCallback(() => {
|
const handleTransfer = useLastCallback(() => {
|
||||||
if (savedGift?.gift.type !== 'starGiftUnique') return;
|
if (savedGift?.gift.type !== 'starGiftUnique') return;
|
||||||
openGiftWithdrawModal({ gift: savedGift });
|
openGiftTransferModal({ gift: savedGift });
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleFocusUpgraded = useLastCallback(() => {
|
const handleFocusUpgraded = useLastCallback(() => {
|
||||||
@ -298,8 +298,8 @@ const GiftInfoModal = ({
|
|||||||
{lang('Share')}
|
{lang('Share')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{canUpdate && isUniqueGift && (
|
{canUpdate && isUniqueGift && (
|
||||||
<MenuItem icon="diamond" onClick={handleWithdraw}>
|
<MenuItem icon="diamond" onClick={handleTransfer}>
|
||||||
{lang('GiftInfoWithdraw')}
|
{lang('GiftInfoTransfer')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
import type { FC } from '../../../../lib/teact/teact';
|
|
||||||
import React, {
|
import React, {
|
||||||
memo, useMemo, useState,
|
memo, useMemo, useState,
|
||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import {
|
import { filterPeersByQuery } from '../../../../global/helpers/peers';
|
||||||
filterUsersByName, isUserBot,
|
import { selectCanGift } from '../../../../global/selectors';
|
||||||
} from '../../../../global/helpers';
|
|
||||||
import { unique } from '../../../../util/iteratees';
|
import { unique } from '../../../../util/iteratees';
|
||||||
import sortChatIds from '../../../common/helpers/sortChatIds';
|
import sortChatIds from '../../../common/helpers/sortChatIds';
|
||||||
|
|
||||||
@ -24,15 +22,14 @@ export type OwnProps = {
|
|||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
currentUserId?: string;
|
currentUserId?: string;
|
||||||
userSelectionLimit?: number;
|
|
||||||
userIds?: string[];
|
userIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const GiftRecipientPicker: FC<OwnProps & StateProps> = ({
|
const GiftRecipientPicker = ({
|
||||||
modal,
|
modal,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
userIds,
|
userIds,
|
||||||
}) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { closeGiftRecipientPicker, openGiftModal } = getActions();
|
const { closeGiftRecipientPicker, openGiftModal } = getActions();
|
||||||
|
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
@ -41,17 +38,12 @@ const GiftRecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||||
|
|
||||||
const displayedUserIds = useMemo(() => {
|
const displayedUserIds = useMemo(() => {
|
||||||
const usersById = getGlobal().users.byId;
|
const global = getGlobal();
|
||||||
const idsWithSelf = userIds ? userIds.concat(currentUserId!) : undefined;
|
const idsWithSelf = userIds ? userIds.concat(currentUserId!) : undefined;
|
||||||
const filteredContactIds = idsWithSelf ? filterUsersByName(idsWithSelf, usersById, searchQuery) : [];
|
const filteredPeerIds = idsWithSelf ? filterPeersByQuery({ ids: idsWithSelf, query: searchQuery }) : [];
|
||||||
|
|
||||||
return sortChatIds(unique(filteredContactIds).filter((userId) => {
|
return sortChatIds(unique(filteredPeerIds).filter((peerId) => {
|
||||||
const user = usersById[userId];
|
return selectCanGift(global, peerId);
|
||||||
if (!user) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !isUserBot(user);
|
|
||||||
}), undefined, [currentUserId!]);
|
}), undefined, [currentUserId!]);
|
||||||
}, [currentUserId, searchQuery, userIds]);
|
}, [currentUserId, searchQuery, userIds]);
|
||||||
|
|
||||||
@ -92,6 +84,5 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
|
|||||||
return {
|
return {
|
||||||
currentUserId,
|
currentUserId,
|
||||||
userIds: global.contactList?.userIds,
|
userIds: global.contactList?.userIds,
|
||||||
userSelectionLimit: global.appConfig?.giveawayAddPeersMax,
|
|
||||||
};
|
};
|
||||||
})(GiftRecipientPicker));
|
})(GiftRecipientPicker));
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import type { FC } from '../../../../lib/teact/teact';
|
||||||
|
import React from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './GiftTransferModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const GiftTransferModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const GiftTransferModal = useModuleLoader(Bundles.Stars, 'GiftTransferModal', !modal);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return GiftTransferModal ? <GiftTransferModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GiftTransferModalAsync;
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.giftPreview {
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backdrop {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
232
src/components/modals/gift/transfer/GiftTransferModal.tsx
Normal file
232
src/components/modals/gift/transfer/GiftTransferModal.tsx
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import React, {
|
||||||
|
memo, useEffect, useMemo, useState,
|
||||||
|
} from '../../../../lib/teact/teact';
|
||||||
|
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
|
import type { ApiStarGiftUnique } from '../../../../api/types';
|
||||||
|
import type { TabState } from '../../../../global/types';
|
||||||
|
import type { UniqueCustomPeer } from '../../../../types';
|
||||||
|
|
||||||
|
import { ALL_FOLDER_ID } from '../../../../config';
|
||||||
|
import { getPeerTitle } from '../../../../global/helpers';
|
||||||
|
import { selectCanGift, selectPeer } from '../../../../global/selectors';
|
||||||
|
import { unique } from '../../../../util/iteratees';
|
||||||
|
import { formatStarsAsIcon, formatStarsAsText } from '../../../../util/localization/format';
|
||||||
|
import { MEMO_EMPTY_ARRAY } from '../../../../util/memo';
|
||||||
|
import { getGiftAttributes } from '../../../common/helpers/gifts';
|
||||||
|
import { REM } from '../../../common/helpers/mediaDimensions';
|
||||||
|
import sortChatIds from '../../../common/helpers/sortChatIds';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||||
|
import { useFolderManagerForOrderedIds } from '../../../../hooks/useFolderManager';
|
||||||
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
|
import usePeerSearch from '../../../../hooks/usePeerSearch';
|
||||||
|
|
||||||
|
import AnimatedIconFromSticker from '../../../common/AnimatedIconFromSticker';
|
||||||
|
import Avatar from '../../../common/Avatar';
|
||||||
|
import Icon from '../../../common/icons/Icon';
|
||||||
|
import PeerPicker from '../../../common/pickers/PeerPicker';
|
||||||
|
import PickerModal from '../../../common/pickers/PickerModal';
|
||||||
|
import RadialPatternBackground from '../../../common/profile/RadialPatternBackground';
|
||||||
|
import ConfirmDialog from '../../../ui/ConfirmDialog';
|
||||||
|
|
||||||
|
import styles from './GiftTransferModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['giftTransferModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
contactIds?: string[];
|
||||||
|
currentUserId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Categories = 'withdraw';
|
||||||
|
|
||||||
|
const AVATAR_SIZE = 4 * REM;
|
||||||
|
const GIFT_STICKER_SIZE = 3 * REM;
|
||||||
|
|
||||||
|
const GiftTransferModal = ({
|
||||||
|
modal, contactIds, currentUserId,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const { closeGiftTransferModal, openGiftWithdrawModal, transferGift } = getActions();
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||||
|
|
||||||
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
const uniqueGift = renderingModal?.gift?.gift as ApiStarGiftUnique;
|
||||||
|
const giftAttributes = uniqueGift && getGiftAttributes(uniqueGift);
|
||||||
|
|
||||||
|
const [selectedId, setSelectedId] = useState<string | undefined>();
|
||||||
|
|
||||||
|
const renderingSelectedPeerId = useCurrentOrPrev(selectedId);
|
||||||
|
const renderingSelectedPeer = useMemo(() => {
|
||||||
|
const global = getGlobal();
|
||||||
|
return renderingSelectedPeerId ? selectPeer(global, renderingSelectedPeerId) : undefined;
|
||||||
|
}, [renderingSelectedPeerId]);
|
||||||
|
|
||||||
|
const orderedChatIds = useFolderManagerForOrderedIds(ALL_FOLDER_ID);
|
||||||
|
|
||||||
|
const sortedLocalIds = useMemo(() => {
|
||||||
|
return unique([
|
||||||
|
...(contactIds || []),
|
||||||
|
...(orderedChatIds || []),
|
||||||
|
]);
|
||||||
|
}, [contactIds, orderedChatIds]);
|
||||||
|
|
||||||
|
const { result: foundIds, currentResultsQuery } = usePeerSearch({
|
||||||
|
query: searchQuery,
|
||||||
|
defaultValue: sortedLocalIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isLoading = currentResultsQuery !== searchQuery;
|
||||||
|
|
||||||
|
const categories = useMemo(() => {
|
||||||
|
if (currentResultsQuery) return MEMO_EMPTY_ARRAY;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
type: 'withdraw',
|
||||||
|
isCustomPeer: true,
|
||||||
|
avatarIcon: 'toncoin',
|
||||||
|
peerColorId: 5,
|
||||||
|
title: lang('GiftTransferTON'),
|
||||||
|
}] satisfies UniqueCustomPeer<Categories>[];
|
||||||
|
}, [lang, currentResultsQuery]);
|
||||||
|
|
||||||
|
const handleCategoryChange = useLastCallback((category: Categories) => {
|
||||||
|
if (category !== 'withdraw') return;
|
||||||
|
|
||||||
|
openGiftWithdrawModal({
|
||||||
|
gift: renderingModal!.gift,
|
||||||
|
});
|
||||||
|
closeGiftTransferModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const displayIds = useMemo(() => {
|
||||||
|
if (isLoading) return MEMO_EMPTY_ARRAY;
|
||||||
|
const global = getGlobal();
|
||||||
|
|
||||||
|
return sortChatIds((foundIds || []).filter((peerId) => (
|
||||||
|
peerId !== currentUserId && selectCanGift(global, peerId)
|
||||||
|
)),
|
||||||
|
false);
|
||||||
|
}, [isLoading, foundIds, currentUserId]);
|
||||||
|
|
||||||
|
const closeConfirmModal = useLastCallback(() => {
|
||||||
|
setSelectedId(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) {
|
||||||
|
setSelectedId(undefined);
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
const handleTransfer = useLastCallback(() => {
|
||||||
|
if (!renderingModal?.gift.inputGift) return;
|
||||||
|
transferGift({
|
||||||
|
gift: renderingModal.gift.inputGift,
|
||||||
|
recipientId: renderingSelectedPeerId!,
|
||||||
|
transferStars: renderingModal.gift.transferStars,
|
||||||
|
});
|
||||||
|
|
||||||
|
closeConfirmModal();
|
||||||
|
closeGiftTransferModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PickerModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={closeGiftTransferModal}
|
||||||
|
title={lang('GiftTransferTitle')}
|
||||||
|
hasCloseButton
|
||||||
|
shouldAdaptToSearch
|
||||||
|
withFixedHeight
|
||||||
|
ignoreFreeze
|
||||||
|
>
|
||||||
|
<PeerPicker<Categories>
|
||||||
|
itemIds={displayIds}
|
||||||
|
categories={categories}
|
||||||
|
onSelectedCategoryChange={handleCategoryChange}
|
||||||
|
withDefaultPadding
|
||||||
|
withPeerUsernames
|
||||||
|
isSearchable
|
||||||
|
noScrollRestore
|
||||||
|
isLoading={isLoading}
|
||||||
|
filterValue={searchQuery}
|
||||||
|
filterPlaceholder={lang('Search')}
|
||||||
|
onFilterChange={setSearchQuery}
|
||||||
|
onSelectedIdChange={setSelectedId}
|
||||||
|
/>
|
||||||
|
{giftAttributes && (
|
||||||
|
<ConfirmDialog
|
||||||
|
isOpen={Boolean(selectedId)}
|
||||||
|
noDefaultTitle
|
||||||
|
onClose={closeConfirmModal}
|
||||||
|
confirmLabel={renderingModal?.gift.transferStars
|
||||||
|
? lang(
|
||||||
|
'GiftTransferConfirmButton',
|
||||||
|
{ amount: formatStarsAsIcon(lang, renderingModal.gift.transferStars, { asFont: true }) },
|
||||||
|
{ withNodes: true },
|
||||||
|
) : lang('GiftTransferConfirmButtonFree')}
|
||||||
|
confirmHandler={handleTransfer}
|
||||||
|
>
|
||||||
|
<div className={styles.header}>
|
||||||
|
<div className={styles.giftPreview}>
|
||||||
|
<RadialPatternBackground
|
||||||
|
className={styles.backdrop}
|
||||||
|
backgroundColors={[giftAttributes.backdrop!.centerColor, giftAttributes.backdrop!.edgeColor]}
|
||||||
|
patternColor={giftAttributes.backdrop?.patternColor}
|
||||||
|
patternIcon={giftAttributes.pattern?.sticker}
|
||||||
|
/>
|
||||||
|
<AnimatedIconFromSticker
|
||||||
|
className={styles.sticker}
|
||||||
|
size={GIFT_STICKER_SIZE}
|
||||||
|
sticker={giftAttributes.model?.sticker}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Icon name="next" className={styles.arrow} />
|
||||||
|
<Avatar
|
||||||
|
peer={renderingSelectedPeer}
|
||||||
|
size={AVATAR_SIZE}
|
||||||
|
className={styles.avatar}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
{renderingModal?.gift.transferStars
|
||||||
|
? lang('GiftTransferConfirmDescription', {
|
||||||
|
gift: lang('GiftUnique', { title: uniqueGift.title, number: uniqueGift.number }),
|
||||||
|
amount: formatStarsAsText(lang, renderingModal.gift.transferStars),
|
||||||
|
peer: getPeerTitle(lang, renderingSelectedPeer!),
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
})
|
||||||
|
: lang('GiftTransferConfirmDescriptionFree', {
|
||||||
|
gift: lang('GiftUnique', { title: uniqueGift.title, number: uniqueGift.number }),
|
||||||
|
peer: getPeerTitle(lang, renderingSelectedPeer!),
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</ConfirmDialog>
|
||||||
|
)}
|
||||||
|
</PickerModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global): StateProps => {
|
||||||
|
const { contactList, currentUserId } = global;
|
||||||
|
|
||||||
|
return {
|
||||||
|
contactIds: contactList?.userIds,
|
||||||
|
currentUserId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(GiftTransferModal));
|
||||||
@ -5,11 +5,11 @@ import React, {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { LoadMoreDirection } from '../../../types';
|
import { LoadMoreDirection } from '../../../types';
|
||||||
|
|
||||||
import { filterUsersByName } from '../../../global/helpers';
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { selectTabState } from '../../../global/selectors';
|
import { selectTabState } from '../../../global/selectors';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
@ -57,8 +57,7 @@ const MoreAppsTabContent: FC<OwnProps & StateProps> = ({
|
|||||||
const filteredFoundIds = useMemo(() => {
|
const filteredFoundIds = useMemo(() => {
|
||||||
if (!foundIds) return [];
|
if (!foundIds) return [];
|
||||||
|
|
||||||
const usersById = getGlobal().users.byId;
|
return filterPeersByQuery({ ids: foundIds, query: searchQuery, type: 'user' });
|
||||||
return filterUsersByName(foundIds, usersById, searchQuery);
|
|
||||||
}, [foundIds, searchQuery]);
|
}, [foundIds, searchQuery]);
|
||||||
|
|
||||||
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
|
||||||
|
|||||||
@ -10,8 +10,9 @@ import type {
|
|||||||
import { NewChatMembersProgress } from '../../types';
|
import { NewChatMembersProgress } from '../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
filterUsersByName, isChatChannel, isUserBot,
|
isChatChannel, isUserBot,
|
||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../global/helpers/peers';
|
||||||
import { selectChat, selectChatFullInfo, selectTabState } from '../../global/selectors';
|
import { selectChat, selectChatFullInfo, selectTabState } from '../../global/selectors';
|
||||||
import { unique } from '../../util/iteratees';
|
import { unique } from '../../util/iteratees';
|
||||||
import sortChatIds from '../common/helpers/sortChatIds';
|
import sortChatIds from '../common/helpers/sortChatIds';
|
||||||
@ -83,14 +84,18 @@ const AddChatMembers: FC<OwnProps & StateProps> = ({
|
|||||||
const displayedIds = useMemo(() => {
|
const displayedIds = useMemo(() => {
|
||||||
// No need for expensive global updates on users, so we avoid them
|
// No need for expensive global updates on users, so we avoid them
|
||||||
const usersById = getGlobal().users.byId;
|
const usersById = getGlobal().users.byId;
|
||||||
const filteredContactIds = localContactIds ? filterUsersByName(localContactIds, usersById, searchQuery) : [];
|
const filteredIds = filterPeersByQuery({
|
||||||
|
ids: unique([
|
||||||
return sortChatIds(
|
...(localContactIds || []),
|
||||||
unique([
|
|
||||||
...filteredContactIds,
|
|
||||||
...(localUserIds || []),
|
...(localUserIds || []),
|
||||||
...(globalUserIds || []),
|
...(globalUserIds || []),
|
||||||
]).filter((userId) => {
|
]),
|
||||||
|
query: searchQuery,
|
||||||
|
type: 'user',
|
||||||
|
});
|
||||||
|
|
||||||
|
return sortChatIds(
|
||||||
|
filteredIds.filter((userId) => {
|
||||||
const user = usersById[userId];
|
const user = usersById[userId];
|
||||||
|
|
||||||
// The user can be added to the chat if the following conditions are met:
|
// The user can be added to the chat if the following conditions are met:
|
||||||
|
|||||||
@ -8,9 +8,10 @@ import type { ApiChatMember, ApiUserStatus } from '../../../api/types';
|
|||||||
import { ManagementScreens, NewChatMembersProgress } from '../../../types';
|
import { ManagementScreens, NewChatMembersProgress } from '../../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
filterUsersByName, getHasAdminRight, isChatBasicGroup,
|
getHasAdminRight, isChatBasicGroup,
|
||||||
isChatChannel, isUserBot, isUserRightBanned, sortUserIds,
|
isChatChannel, isUserBot, isUserRightBanned, sortUserIds,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { selectChat, selectChatFullInfo, selectTabState } from '../../../global/selectors';
|
import { selectChat, selectChatFullInfo, selectTabState } from '../../../global/selectors';
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
import sortChatIds from '../../common/helpers/sortChatIds';
|
import sortChatIds from '../../common/helpers/sortChatIds';
|
||||||
@ -121,7 +122,7 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
|||||||
const shouldUseSearchResults = Boolean(searchQuery);
|
const shouldUseSearchResults = Boolean(searchQuery);
|
||||||
const listedIds = !shouldUseSearchResults
|
const listedIds = !shouldUseSearchResults
|
||||||
? memberIds
|
? memberIds
|
||||||
: (localContactIds ? filterUsersByName(localContactIds, usersById, searchQuery) : []);
|
: (localContactIds ? filterPeersByQuery({ ids: localContactIds, query: searchQuery, type: 'user' }) : []);
|
||||||
|
|
||||||
return sortChatIds(
|
return sortChatIds(
|
||||||
unique([
|
unique([
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import useInterval from '../../../hooks/schedulers/useInterval';
|
|||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useForceUpdate from '../../../hooks/useForceUpdate';
|
import useForceUpdate from '../../../hooks/useForceUpdate';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||||
@ -72,7 +73,8 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
setOpenedInviteInfo,
|
setOpenedInviteInfo,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const lang = useOldLang();
|
const lang = useLang();
|
||||||
|
const oldLang = useOldLang();
|
||||||
|
|
||||||
const [isDeleteRevokeAllDialogOpen, openDeleteRevokeAllDialog, closeDeleteRevokeAllDialog] = useFlag();
|
const [isDeleteRevokeAllDialogOpen, openDeleteRevokeAllDialog, closeDeleteRevokeAllDialog] = useFlag();
|
||||||
const [isRevokeDialogOpen, openRevokeDialog, closeRevokeDialog] = useFlag();
|
const [isRevokeDialogOpen, openRevokeDialog, closeRevokeDialog] = useFlag();
|
||||||
@ -174,9 +176,9 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
const copyLink = useCallback((link: string) => {
|
const copyLink = useCallback((link: string) => {
|
||||||
copyTextToClipboard(link);
|
copyTextToClipboard(link);
|
||||||
showNotification({
|
showNotification({
|
||||||
message: lang('LinkCopied'),
|
message: oldLang('LinkCopied'),
|
||||||
});
|
});
|
||||||
}, [lang, showNotification]);
|
}, [oldLang, showNotification]);
|
||||||
|
|
||||||
const prepareUsageText = (invite: ApiExportedInvite) => {
|
const prepareUsageText = (invite: ApiExportedInvite) => {
|
||||||
const {
|
const {
|
||||||
@ -184,34 +186,34 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
} = invite;
|
} = invite;
|
||||||
let text = '';
|
let text = '';
|
||||||
if (!isRevoked && usageLimit && usage < usageLimit) {
|
if (!isRevoked && usageLimit && usage < usageLimit) {
|
||||||
text = lang('CanJoin', usageLimit - usage);
|
text = oldLang('CanJoin', usageLimit - usage);
|
||||||
} else if (usage) {
|
} else if (usage) {
|
||||||
text = lang('PeopleJoined', usage);
|
text = oldLang('PeopleJoined', usage);
|
||||||
} else {
|
} else {
|
||||||
text = lang('NoOneJoined');
|
text = oldLang('NoOneJoined');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRevoked) {
|
if (isRevoked) {
|
||||||
text += ` ${BULLET} ${lang('Revoked')}`;
|
text += ` ${BULLET} ${oldLang('Revoked')}`;
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requested) {
|
if (requested) {
|
||||||
text += ` ${BULLET} ${lang('JoinRequests', requested)}`;
|
text += ` ${BULLET} ${oldLang('JoinRequests', requested)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usageLimit !== undefined && usage === usageLimit) {
|
if (usageLimit !== undefined && usage === usageLimit) {
|
||||||
text += ` ${BULLET} ${lang('LinkLimitReached')}`;
|
text += ` ${BULLET} ${oldLang('LinkLimitReached')}`;
|
||||||
} else if (expireDate) {
|
} else if (expireDate) {
|
||||||
const diff = (expireDate - getServerTime()) * 1000;
|
const diff = expireDate - getServerTime();
|
||||||
text += ` ${BULLET} `;
|
text += ` ${BULLET} `;
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
text += lang('InviteLink.ExpiresIn', formatCountdown(lang, diff));
|
text += oldLang('InviteLink.ExpiresIn', formatCountdown(lang, diff));
|
||||||
} else {
|
} else {
|
||||||
text += lang('InviteLink.Expired');
|
text += oldLang('InviteLink.Expired');
|
||||||
}
|
}
|
||||||
} else if (isPermanent) {
|
} else if (isPermanent) {
|
||||||
text += ` ${BULLET} ${lang('Permanent')}`;
|
text += ` ${BULLET} ${oldLang('Permanent')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
@ -239,14 +241,14 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
const prepareContextActions = (invite: ApiExportedInvite) => {
|
const prepareContextActions = (invite: ApiExportedInvite) => {
|
||||||
const actions: MenuItemContextAction[] = [];
|
const actions: MenuItemContextAction[] = [];
|
||||||
actions.push({
|
actions.push({
|
||||||
title: lang('Copy'),
|
title: oldLang('Copy'),
|
||||||
icon: 'copy',
|
icon: 'copy',
|
||||||
handler: () => copyLink(invite.link),
|
handler: () => copyLink(invite.link),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!invite.isPermanent && !invite.isRevoked) {
|
if (!invite.isPermanent && !invite.isRevoked) {
|
||||||
actions.push({
|
actions.push({
|
||||||
title: lang('Edit'),
|
title: oldLang('Edit'),
|
||||||
icon: 'edit',
|
icon: 'edit',
|
||||||
handler: () => editInvite(invite),
|
handler: () => editInvite(invite),
|
||||||
});
|
});
|
||||||
@ -254,14 +256,14 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
if (!invite.isRevoked) {
|
if (!invite.isRevoked) {
|
||||||
actions.push({
|
actions.push({
|
||||||
title: lang('RevokeButton'),
|
title: oldLang('RevokeButton'),
|
||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
handler: () => askToRevoke(invite),
|
handler: () => askToRevoke(invite),
|
||||||
destructive: true,
|
destructive: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
actions.push({
|
actions.push({
|
||||||
title: lang('DeleteLink'),
|
title: oldLang('DeleteLink'),
|
||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
handler: () => askToDelete(invite),
|
handler: () => askToDelete(invite),
|
||||||
destructive: true,
|
destructive: true,
|
||||||
@ -279,7 +281,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
size={STICKER_SIZE_INVITES}
|
size={STICKER_SIZE_INVITES}
|
||||||
className="section-icon"
|
className="section-icon"
|
||||||
/>
|
/>
|
||||||
<p className="section-help">{isChannel ? lang('PrimaryLinkHelpChannel') : lang('PrimaryLinkHelp')}</p>
|
<p className="section-help">{isChannel ? oldLang('PrimaryLinkHelpChannel') : oldLang('PrimaryLinkHelp')}</p>
|
||||||
</div>
|
</div>
|
||||||
{primaryInviteLink && (
|
{primaryInviteLink && (
|
||||||
<div className="section">
|
<div className="section">
|
||||||
@ -288,13 +290,13 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
link={primaryInviteLink}
|
link={primaryInviteLink}
|
||||||
withShare
|
withShare
|
||||||
onRevoke={!chat?.usernames ? handlePrimaryRevoke : undefined}
|
onRevoke={!chat?.usernames ? handlePrimaryRevoke : undefined}
|
||||||
title={chat?.usernames ? lang('PublicLink') : lang('lng_create_permanent_link_title')}
|
title={chat?.usernames ? oldLang('PublicLink') : oldLang('lng_create_permanent_link_title')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="section" teactFastList>
|
<div className="section" teactFastList>
|
||||||
<Button isText key="create" className="create-link" onClick={handleCreateNewClick}>
|
<Button isText key="create" className="create-link" onClick={handleCreateNewClick}>
|
||||||
{lang('CreateNewLink')}
|
{oldLang('CreateNewLink')}
|
||||||
</Button>
|
</Button>
|
||||||
{(!temporalInvites || !temporalInvites.length) && <NothingFound text="No links found" key="nothing" />}
|
{(!temporalInvites || !temporalInvites.length) && <NothingFound text="No links found" key="nothing" />}
|
||||||
{temporalInvites?.map((invite) => (
|
{temporalInvites?.map((invite) => (
|
||||||
@ -313,18 +315,18 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
<p className="section-help hint" key="links-hint">{lang('ManageLinksInfoHelp')}</p>
|
<p className="section-help hint" key="links-hint">{oldLang('ManageLinksInfoHelp')}</p>
|
||||||
</div>
|
</div>
|
||||||
{revokedExportedInvites && Boolean(revokedExportedInvites.length) && (
|
{revokedExportedInvites && Boolean(revokedExportedInvites.length) && (
|
||||||
<div className="section" teactFastList>
|
<div className="section" teactFastList>
|
||||||
<p className="section-help" key="title">{lang('RevokedLinks')}</p>
|
<p className="section-help" key="title">{oldLang('RevokedLinks')}</p>
|
||||||
<ListItem
|
<ListItem
|
||||||
icon="delete"
|
icon="delete"
|
||||||
destructive
|
destructive
|
||||||
key="delete"
|
key="delete"
|
||||||
onClick={openDeleteRevokeAllDialog}
|
onClick={openDeleteRevokeAllDialog}
|
||||||
>
|
>
|
||||||
<span className="title">{lang('DeleteAllRevokedLinks')}</span>
|
<span className="title">{oldLang('DeleteAllRevokedLinks')}</span>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{revokedExportedInvites?.map((invite) => (
|
{revokedExportedInvites?.map((invite) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
@ -348,28 +350,28 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isDeleteRevokeAllDialogOpen}
|
isOpen={isDeleteRevokeAllDialogOpen}
|
||||||
onClose={closeDeleteRevokeAllDialog}
|
onClose={closeDeleteRevokeAllDialog}
|
||||||
title={lang('DeleteAllRevokedLinks')}
|
title={oldLang('DeleteAllRevokedLinks')}
|
||||||
text={lang('DeleteAllRevokedLinkHelp')}
|
text={oldLang('DeleteAllRevokedLinkHelp')}
|
||||||
confirmIsDestructive
|
confirmIsDestructive
|
||||||
confirmLabel={lang('DeleteAll')}
|
confirmLabel={oldLang('DeleteAll')}
|
||||||
confirmHandler={handleDeleteAllRevoked}
|
confirmHandler={handleDeleteAllRevoked}
|
||||||
/>
|
/>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isRevokeDialogOpen}
|
isOpen={isRevokeDialogOpen}
|
||||||
onClose={closeRevokeDialog}
|
onClose={closeRevokeDialog}
|
||||||
title={lang('RevokeLink')}
|
title={oldLang('RevokeLink')}
|
||||||
text={lang('RevokeAlert')}
|
text={oldLang('RevokeAlert')}
|
||||||
confirmIsDestructive
|
confirmIsDestructive
|
||||||
confirmLabel={lang('RevokeButton')}
|
confirmLabel={oldLang('RevokeButton')}
|
||||||
confirmHandler={handleRevoke}
|
confirmHandler={handleRevoke}
|
||||||
/>
|
/>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isDeleteDialogOpen}
|
isOpen={isDeleteDialogOpen}
|
||||||
onClose={closeDeleteDialog}
|
onClose={closeDeleteDialog}
|
||||||
title={lang('DeleteLink')}
|
title={oldLang('DeleteLink')}
|
||||||
text={lang('DeleteLinkHelp')}
|
text={oldLang('DeleteLinkHelp')}
|
||||||
confirmIsDestructive
|
confirmIsDestructive
|
||||||
confirmLabel={lang('Delete')}
|
confirmLabel={oldLang('Delete')}
|
||||||
confirmHandler={handleDelete}
|
confirmHandler={handleDelete}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,11 +3,11 @@ import React, {
|
|||||||
memo, useCallback,
|
memo, useCallback,
|
||||||
useMemo, useState,
|
useMemo, useState,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiChat, ApiChatMember } from '../../../api/types';
|
import type { ApiChat, ApiChatMember } from '../../../api/types';
|
||||||
|
|
||||||
import { filterUsersByName } from '../../../global/helpers';
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import { selectChatFullInfo } from '../../../global/selectors';
|
import { selectChatFullInfo } from '../../../global/selectors';
|
||||||
|
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
@ -49,10 +49,7 @@ const RemoveGroupUserModal: FC<OwnProps & StateProps> = ({
|
|||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// No need for expensive global updates on users, so we avoid them
|
return filterPeersByQuery({ ids: availableMemberIds, query: search, type: 'user' });
|
||||||
const usersById = getGlobal().users.byId;
|
|
||||||
|
|
||||||
return filterUsersByName(availableMemberIds, usersById, search);
|
|
||||||
}, [chatMembers, currentUserId, search]);
|
}, [chatMembers, currentUserId, search]);
|
||||||
|
|
||||||
const handleRemoveUser = useCallback((userId: string) => {
|
const handleRemoveUser = useCallback((userId: string) => {
|
||||||
|
|||||||
@ -278,7 +278,6 @@ function StorySettings({
|
|||||||
id="deny-list"
|
id="deny-list"
|
||||||
contactListIds={contactListIds}
|
contactListIds={contactListIds}
|
||||||
currentUserId={currentUserId}
|
currentUserId={currentUserId}
|
||||||
usersById={usersById}
|
|
||||||
selectedIds={selectedBlockedIds}
|
selectedIds={selectedBlockedIds}
|
||||||
onSelect={handleDenyUserIdsChange}
|
onSelect={handleDenyUserIdsChange}
|
||||||
/>
|
/>
|
||||||
@ -291,7 +290,6 @@ function StorySettings({
|
|||||||
contactListIds={contactListIds}
|
contactListIds={contactListIds}
|
||||||
lockedIds={lockedIds}
|
lockedIds={lockedIds}
|
||||||
currentUserId={currentUserId}
|
currentUserId={currentUserId}
|
||||||
usersById={usersById}
|
|
||||||
selectedIds={privacy?.allowUserIds}
|
selectedIds={privacy?.allowUserIds}
|
||||||
onSelect={handleAllowUserIdsChange}
|
onSelect={handleAllowUserIdsChange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import React, { memo, useMemo, useState } from '../../../lib/teact/teact';
|
import React, { memo, useMemo, useState } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiUser } from '../../../api/types';
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
|
|
||||||
import { filterUsersByName } from '../../../global/helpers';
|
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||||
|
|
||||||
@ -16,7 +14,6 @@ interface OwnProps {
|
|||||||
currentUserId: string;
|
currentUserId: string;
|
||||||
selectedIds?: string[];
|
selectedIds?: string[];
|
||||||
lockedIds?: string[];
|
lockedIds?: string[];
|
||||||
usersById: Record<string, ApiUser>;
|
|
||||||
onSelect: (selectedIds: string[]) => void;
|
onSelect: (selectedIds: string[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +21,6 @@ function AllowDenyList({
|
|||||||
id,
|
id,
|
||||||
contactListIds,
|
contactListIds,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
usersById,
|
|
||||||
selectedIds,
|
selectedIds,
|
||||||
lockedIds,
|
lockedIds,
|
||||||
onSelect,
|
onSelect,
|
||||||
@ -34,8 +30,8 @@ function AllowDenyList({
|
|||||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||||
const displayedIds = useMemo(() => {
|
const displayedIds = useMemo(() => {
|
||||||
const contactIds = (contactListIds || []).filter((userId) => userId !== currentUserId);
|
const contactIds = (contactListIds || []).filter((userId) => userId !== currentUserId);
|
||||||
return unique(filterUsersByName([...selectedIds || [], ...contactIds], usersById, searchQuery));
|
return unique(filterPeersByQuery({ ids: [...selectedIds || [], ...contactIds], query: searchQuery, type: 'user' }));
|
||||||
}, [contactListIds, currentUserId, searchQuery, selectedIds, usersById]);
|
}, [contactListIds, currentUserId, searchQuery, selectedIds]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PeerPicker
|
<PeerPicker
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { getActions } from '../../../global';
|
|||||||
|
|
||||||
import type { ApiUser } from '../../../api/types';
|
import type { ApiUser } from '../../../api/types';
|
||||||
|
|
||||||
import { filterUsersByName } from '../../../global/helpers';
|
import { filterPeersByQuery } from '../../../global/helpers/peers';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { unique } from '../../../util/iteratees';
|
import { unique } from '../../../util/iteratees';
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ function CloseFriends({
|
|||||||
|
|
||||||
const displayedIds = useMemo(() => {
|
const displayedIds = useMemo(() => {
|
||||||
const contactIds = (contactListIds || []).filter((id) => id !== currentUserId);
|
const contactIds = (contactListIds || []).filter((id) => id !== currentUserId);
|
||||||
return unique(filterUsersByName([...closeFriendIds, ...contactIds], usersById, searchQuery));
|
return unique(filterPeersByQuery({ ids: [...closeFriendIds, ...contactIds], query: searchQuery, type: 'user' }));
|
||||||
}, [closeFriendIds, contactListIds, currentUserId, searchQuery, usersById]);
|
}, [closeFriendIds, contactListIds, currentUserId, searchQuery]);
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevIsActive]) => {
|
useEffectWithPrevDeps(([prevIsActive]) => {
|
||||||
if (!prevIsActive && isActive) {
|
if (!prevIsActive && isActive) {
|
||||||
|
|||||||
@ -14,10 +14,11 @@ import Modal from './Modal';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
noDefaultTitle?: boolean;
|
||||||
header?: TeactNode;
|
header?: TeactNode;
|
||||||
textParts?: TextPart;
|
textParts?: TextPart;
|
||||||
text?: string;
|
text?: string;
|
||||||
confirmLabel?: string;
|
confirmLabel?: TeactNode;
|
||||||
confirmIsDestructive?: boolean;
|
confirmIsDestructive?: boolean;
|
||||||
isConfirmDisabled?: boolean;
|
isConfirmDisabled?: boolean;
|
||||||
isOnlyConfirm?: boolean;
|
isOnlyConfirm?: boolean;
|
||||||
@ -32,6 +33,7 @@ type OwnProps = {
|
|||||||
const ConfirmDialog: FC<OwnProps> = ({
|
const ConfirmDialog: FC<OwnProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
title,
|
title,
|
||||||
|
noDefaultTitle,
|
||||||
header,
|
header,
|
||||||
text,
|
text,
|
||||||
textParts,
|
textParts,
|
||||||
@ -60,7 +62,7 @@ const ConfirmDialog: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className={buildClassName('confirm', className)}
|
className={buildClassName('confirm', className)}
|
||||||
title={(title || lang('Telegram'))}
|
title={(title || (!noDefaultTitle ? lang('Telegram') : undefined))}
|
||||||
header={header}
|
header={header}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
|||||||
@ -42,6 +42,7 @@ export type OwnProps = {
|
|||||||
dialogRef?: React.RefObject<HTMLDivElement>;
|
dialogRef?: React.RefObject<HTMLDivElement>;
|
||||||
isLowStackPriority?: boolean;
|
isLowStackPriority?: boolean;
|
||||||
dialogContent?: React.ReactNode;
|
dialogContent?: React.ReactNode;
|
||||||
|
ignoreFreeze?: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onCloseAnimationEnd?: () => void;
|
onCloseAnimationEnd?: () => void;
|
||||||
onEnter?: () => void;
|
onEnter?: () => void;
|
||||||
|
|||||||
@ -1941,7 +1941,7 @@ addActionHandler('loadMoreMembers', async (global, actions, payload): Promise<vo
|
|||||||
const offset = selectChatFullInfo(global, chat.id)?.members?.length;
|
const offset = selectChatFullInfo(global, chat.id)?.members?.length;
|
||||||
if (offset !== undefined && chat.membersCount !== undefined && offset >= chat.membersCount) return;
|
if (offset !== undefined && chat.membersCount !== undefined && offset >= chat.membersCount) return;
|
||||||
|
|
||||||
const result = await callApi('fetchMembers', chat.id, chat.accessHash!, 'recent', offset);
|
const result = await callApi('fetchMembers', { chat, offset });
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type {
|
import type {
|
||||||
ApiInputInvoice, ApiInputInvoiceStarGift, ApiInputInvoiceStarGiftUpgrade, ApiRequestInputInvoice,
|
ApiInputInvoice, ApiInputInvoiceStarGift, ApiRequestInputInvoice,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { ApiCredentials } from '../../../components/payment/PaymentModal';
|
import type { ApiCredentials } from '../../../components/payment/PaymentModal';
|
||||||
import type { RegularLangFnParameters } from '../../../util/localization';
|
import type { RegularLangFnParameters } from '../../../util/localization';
|
||||||
@ -977,7 +977,7 @@ addActionHandler('upgradeGift', (global, actions, payload): ActionReturnType =>
|
|||||||
actions.closeGiftInfoModal({ tabId });
|
actions.closeGiftInfoModal({ tabId });
|
||||||
|
|
||||||
if (!upgradeStars) {
|
if (!upgradeStars) {
|
||||||
callApi('upgradeGift', {
|
callApi('upgradeStarGift', {
|
||||||
inputSavedGift: requestSavedGift,
|
inputSavedGift: requestSavedGift,
|
||||||
shouldKeepOriginalDetails: shouldKeepOriginalDetails || undefined,
|
shouldKeepOriginalDetails: shouldKeepOriginalDetails || undefined,
|
||||||
});
|
});
|
||||||
@ -985,7 +985,7 @@ addActionHandler('upgradeGift', (global, actions, payload): ActionReturnType =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoice: ApiInputInvoiceStarGiftUpgrade = {
|
const invoice: ApiInputInvoice = {
|
||||||
type: 'stargiftUpgrade',
|
type: 'stargiftUpgrade',
|
||||||
inputSavedGift: gift,
|
inputSavedGift: gift,
|
||||||
shouldKeepOriginalDetails: shouldKeepOriginalDetails || undefined,
|
shouldKeepOriginalDetails: shouldKeepOriginalDetails || undefined,
|
||||||
@ -994,6 +994,46 @@ addActionHandler('upgradeGift', (global, actions, payload): ActionReturnType =>
|
|||||||
payInputStarInvoice(global, invoice, upgradeStars, tabId);
|
payInputStarInvoice(global, invoice, upgradeStars, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('transferGift', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
gift, recipientId, transferStars, tabId = getCurrentTabId(),
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const peer = selectChat(global, recipientId);
|
||||||
|
|
||||||
|
const requestSavedGift = getRequestInputSavedStarGift(global, gift);
|
||||||
|
if (!peer || !requestSavedGift) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
isWaitingForStarGiftTransfer: true,
|
||||||
|
}, tabId);
|
||||||
|
|
||||||
|
setGlobal(global);
|
||||||
|
global = getGlobal();
|
||||||
|
|
||||||
|
actions.closeGiftTransferModal({ tabId });
|
||||||
|
actions.closeGiftInfoModal({ tabId });
|
||||||
|
|
||||||
|
if (!transferStars) {
|
||||||
|
callApi('transferStarGift', {
|
||||||
|
inputSavedGift: requestSavedGift,
|
||||||
|
toPeer: peer,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoice: ApiInputInvoice = {
|
||||||
|
type: 'stargiftTransfer',
|
||||||
|
inputSavedGift: gift,
|
||||||
|
recipientId,
|
||||||
|
};
|
||||||
|
|
||||||
|
payInputStarInvoice(global, invoice, transferStars, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
async function payInputStarInvoice<T extends GlobalState>(
|
async function payInputStarInvoice<T extends GlobalState>(
|
||||||
global: T, inputInvoice: ApiInputInvoice, price: number,
|
global: T, inputInvoice: ApiInputInvoice, price: number,
|
||||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
|||||||
@ -2,7 +2,8 @@ import type { ActionReturnType } from '../../types';
|
|||||||
import { PaymentStep } from '../../../types';
|
import { PaymentStep } from '../../../types';
|
||||||
|
|
||||||
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
||||||
import { applyLangPackDifference, requestLangPackDifference } from '../../../util/localization';
|
import { applyLangPackDifference, getTranslationFn, requestLangPackDifference } from '../../../util/localization';
|
||||||
|
import { getPeerTitle } from '../../helpers';
|
||||||
import { addActionHandler, setGlobal } from '../../index';
|
import { addActionHandler, setGlobal } from '../../index';
|
||||||
import {
|
import {
|
||||||
addBlockedUser,
|
addBlockedUser,
|
||||||
@ -21,7 +22,12 @@ import {
|
|||||||
updateThreadInfos,
|
updateThreadInfos,
|
||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
import { updateTabState } from '../../reducers/tabs';
|
import { updateTabState } from '../../reducers/tabs';
|
||||||
import { selectPeerStories, selectPeerStory, selectTabState } from '../../selectors';
|
import {
|
||||||
|
selectPeer,
|
||||||
|
selectPeerStories,
|
||||||
|
selectPeerStory,
|
||||||
|
selectTabState,
|
||||||
|
} from '../../selectors';
|
||||||
|
|
||||||
addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||||
switch (update['@type']) {
|
switch (update['@type']) {
|
||||||
@ -235,7 +241,44 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
isWaitingForStarGiftUpgrade: undefined,
|
isWaitingForStarGiftUpgrade: undefined,
|
||||||
}, tabId);
|
}, tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tabState.isWaitingForStarGiftTransfer) {
|
||||||
|
const chatId = update.message.chatId;
|
||||||
|
const receiver = chatId ? selectPeer(global, chatId) : undefined;
|
||||||
|
if (receiver) {
|
||||||
|
actions.focusMessage({
|
||||||
|
chatId: receiver.id,
|
||||||
|
messageId: update.message.id!,
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
|
||||||
|
actions.showNotification({
|
||||||
|
message: {
|
||||||
|
key: 'GiftTransferSuccessMessage',
|
||||||
|
variables: {
|
||||||
|
gift: {
|
||||||
|
key: 'GiftUnique',
|
||||||
|
variables: {
|
||||||
|
title: actionStarGift.gift.title,
|
||||||
|
number: actionStarGift.gift.number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
peer: getPeerTitle(getTranslationFn(), receiver),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.requestConfetti({ withStars: true, tabId });
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
isWaitingForStarGiftTransfer: undefined,
|
||||||
|
}, tabId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setGlobal(global);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { ActionReturnType } from '../../types';
|
|||||||
|
|
||||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||||
import * as langProvider from '../../../util/oldLangProvider';
|
import * as langProvider from '../../../util/oldLangProvider';
|
||||||
|
import { addTabStateResetterAction } from '../../helpers/meta';
|
||||||
import { getPrizeStarsTransactionFromGiveaway, getStarsTransactionFromGift } from '../../helpers/payments';
|
import { getPrizeStarsTransactionFromGiveaway, getStarsTransactionFromGift } from '../../helpers/payments';
|
||||||
import { addActionHandler } from '../../index';
|
import { addActionHandler } from '../../index';
|
||||||
import {
|
import {
|
||||||
@ -63,13 +64,7 @@ addActionHandler('openGiftRecipientPicker', (global, actions, payload): ActionRe
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('closeGiftRecipientPicker', (global, actions, payload): ActionReturnType => {
|
addTabStateResetterAction('closeGiftRecipientPicker', 'isGiftRecipientPickerOpen');
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
return updateTabState(global, {
|
|
||||||
isGiftRecipientPickerOpen: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('openStarsGiftingPickerModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openStarsGiftingPickerModal', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
@ -83,13 +78,7 @@ addActionHandler('openStarsGiftingPickerModal', (global, actions, payload): Acti
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('closeStarsGiftingPickerModal', (global, actions, payload): ActionReturnType => {
|
addTabStateResetterAction('closeStarsGiftingPickerModal', 'starsGiftingPickerModal');
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
return updateTabState(global, {
|
|
||||||
starsGiftingPickerModal: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('openPrizeStarsTransactionFromGiveaway', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openPrizeStarsTransactionFromGiveaway', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
@ -148,13 +137,7 @@ addActionHandler('openStarsBalanceModal', (global, actions, payload): ActionRetu
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('closeStarsBalanceModal', (global, actions, payload): ActionReturnType => {
|
addTabStateResetterAction('closeStarsBalanceModal', 'starsBalanceModal');
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
return updateTabState(global, {
|
|
||||||
starsBalanceModal: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('closeStarsPaymentModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('closeStarsPaymentModal', (global, actions, payload): ActionReturnType => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
@ -193,13 +176,7 @@ addActionHandler('openStarsTransactionFromGift', (global, actions, payload): Act
|
|||||||
return openStarsTransactionModal(global, transaction, tabId);
|
return openStarsTransactionModal(global, transaction, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('closeStarsTransactionModal', (global, actions, payload): ActionReturnType => {
|
addTabStateResetterAction('closeStarsTransactionModal', 'starsTransactionModal');
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
return updateTabState(global, {
|
|
||||||
starsTransactionModal: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('openStarsSubscriptionModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openStarsSubscriptionModal', (global, actions, payload): ActionReturnType => {
|
||||||
const { subscription, tabId = getCurrentTabId() } = payload;
|
const { subscription, tabId = getCurrentTabId() } = payload;
|
||||||
@ -211,20 +188,9 @@ addActionHandler('openStarsSubscriptionModal', (global, actions, payload): Actio
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('closeStarsSubscriptionModal', (global, actions, payload): ActionReturnType => {
|
addTabStateResetterAction('closeStarsSubscriptionModal', 'starsSubscriptionModal');
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
return updateTabState(global, {
|
addTabStateResetterAction('closeGiftModal', 'giftModal');
|
||||||
starsSubscriptionModal: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('closeGiftModal', (global, actions, payload): ActionReturnType => {
|
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
return updateTabState(global, {
|
|
||||||
giftModal: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('closeStarsGiftModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('closeStarsGiftModal', (global, actions, payload): ActionReturnType => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
@ -287,21 +253,9 @@ addActionHandler('openGiftInfoModal', (global, actions, payload): ActionReturnTy
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('closeGiftInfoModal', (global, actions, payload): ActionReturnType => {
|
addTabStateResetterAction('closeGiftInfoModal', 'giftInfoModal');
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
return updateTabState(global, {
|
addTabStateResetterAction('closeGiftUpgradeModal', 'giftUpgradeModal');
|
||||||
giftInfoModal: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('closeGiftUpgradeModal', (global, actions, payload): ActionReturnType => {
|
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
return updateTabState(global, {
|
|
||||||
giftUpgradeModal: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('openGiftWithdrawModal', (global, actions, payload): ActionReturnType => {
|
addActionHandler('openGiftWithdrawModal', (global, actions, payload): ActionReturnType => {
|
||||||
const { gift, tabId = getCurrentTabId() } = payload || {};
|
const { gift, tabId = getCurrentTabId() } = payload || {};
|
||||||
@ -313,13 +267,7 @@ addActionHandler('openGiftWithdrawModal', (global, actions, payload): ActionRetu
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('closeGiftWithdrawModal', (global, actions, payload): ActionReturnType => {
|
addTabStateResetterAction('closeGiftWithdrawModal', 'giftWithdrawModal');
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
|
||||||
|
|
||||||
return updateTabState(global, {
|
|
||||||
giftWithdrawModal: undefined,
|
|
||||||
}, tabId);
|
|
||||||
});
|
|
||||||
|
|
||||||
addActionHandler('clearGiftWithdrawError', (global, actions, payload): ActionReturnType => {
|
addActionHandler('clearGiftWithdrawError', (global, actions, payload): ActionReturnType => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
@ -334,3 +282,15 @@ addActionHandler('clearGiftWithdrawError', (global, actions, payload): ActionRet
|
|||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('openGiftTransferModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { gift, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
giftTransferModal: {
|
||||||
|
gift,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addTabStateResetterAction('closeGiftTransferModal', 'giftTransferModal');
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import {
|
|||||||
VERIFICATION_CODES_USER_ID,
|
VERIFICATION_CODES_USER_ID,
|
||||||
} from '../../config';
|
} from '../../config';
|
||||||
import { formatDateToString, formatTime } from '../../util/dates/dateFormat';
|
import { formatDateToString, formatTime } from '../../util/dates/dateFormat';
|
||||||
import { prepareSearchWordsForNeedle } from '../../util/searchWords';
|
|
||||||
import { getGlobal } from '..';
|
import { getGlobal } from '..';
|
||||||
import { isSystemBot } from './bots';
|
import { isSystemBot } from './bots';
|
||||||
import { getMainUsername, getUserFirstOrLastName } from './users';
|
import { getMainUsername, getUserFirstOrLastName } from './users';
|
||||||
@ -395,36 +394,6 @@ export function getMessageSenderName(lang: OldLangFn, chatId: string, sender?: A
|
|||||||
return getUserFirstOrLastName(sender);
|
return getUserFirstOrLastName(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterChatsByName(
|
|
||||||
lang: OldLangFn,
|
|
||||||
chatIds: string[],
|
|
||||||
chatsById: Record<string, ApiChat>,
|
|
||||||
query?: string,
|
|
||||||
currentUserId?: string,
|
|
||||||
) {
|
|
||||||
if (!query) {
|
|
||||||
return chatIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchWords = prepareSearchWordsForNeedle(query);
|
|
||||||
|
|
||||||
return chatIds.filter((id) => {
|
|
||||||
const chat = chatsById[id];
|
|
||||||
if (!chat) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const isSelf = id === currentUserId;
|
|
||||||
|
|
||||||
const translatedTitle = getChatTitle(lang, chat, isSelf);
|
|
||||||
if (isSelf) {
|
|
||||||
// Search both "Saved Messages" and user title
|
|
||||||
return searchWords(translatedTitle) || searchWords(chat.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchWords(translatedTitle) || Boolean(chat.usernames?.find(({ username }) => searchWords(username)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isChatPublic(chat: ApiChat) {
|
export function isChatPublic(chat: ApiChat) {
|
||||||
return chat.usernames?.some(({ isActive }) => isActive);
|
return chat.usernames?.some(({ isActive }) => isActive);
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/global/helpers/meta.ts
Normal file
18
src/global/helpers/meta.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { ActionReturnType, TabState } from '../types';
|
||||||
|
|
||||||
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
|
import { updateTabState } from '../reducers/tabs';
|
||||||
|
import { addActionHandler, type TabStateActionNames } from '..';
|
||||||
|
|
||||||
|
export function addTabStateResetterAction<ActionName extends TabStateActionNames>(
|
||||||
|
name: ActionName, key: keyof TabState,
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
addActionHandler(name, (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
[key]: undefined,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -188,6 +188,19 @@ export function getRequestInputInvoice<T extends GlobalState>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inputInvoice.type === 'stargiftTransfer') {
|
||||||
|
const { inputSavedGift, recipientId } = inputInvoice;
|
||||||
|
const savedGift = getRequestInputSavedStarGift(global, inputSavedGift);
|
||||||
|
const peer = selectPeer(global, recipientId);
|
||||||
|
if (!savedGift || !peer) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'stargiftTransfer',
|
||||||
|
inputSavedGift: savedGift,
|
||||||
|
recipient: peer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
import type { ApiChat, ApiPeer, ApiUser } from '../../api/types';
|
import type { ApiChat, ApiPeer, ApiUser } from '../../api/types';
|
||||||
|
|
||||||
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../config';
|
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../config';
|
||||||
|
import { getTranslationFn } from '../../util/localization';
|
||||||
|
import { prepareSearchWordsForNeedle } from '../../util/searchWords';
|
||||||
|
import { selectChat, selectPeer, selectUser } from '../selectors';
|
||||||
|
import { getGlobal } from '..';
|
||||||
|
import { getChatTitle } from './chats';
|
||||||
|
import { getPeerFullTitle } from './messages';
|
||||||
|
|
||||||
export function isApiPeerChat(peer: ApiPeer): peer is ApiChat {
|
export function isApiPeerChat(peer: ApiPeer): peer is ApiChat {
|
||||||
return 'title' in peer;
|
return 'title' in peer;
|
||||||
@ -10,6 +16,44 @@ export function isApiPeerUser(peer: ApiPeer): peer is ApiUser {
|
|||||||
return !isApiPeerChat(peer);
|
return !isApiPeerChat(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function filterPeersByQuery({
|
||||||
|
ids,
|
||||||
|
query,
|
||||||
|
type = 'peer',
|
||||||
|
} : {
|
||||||
|
ids: string[];
|
||||||
|
query: string | undefined;
|
||||||
|
type?: 'chat' | 'user' | 'peer';
|
||||||
|
}) {
|
||||||
|
if (!query) {
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
const global = getGlobal();
|
||||||
|
const lang = getTranslationFn();
|
||||||
|
|
||||||
|
const searchWords = prepareSearchWordsForNeedle(query);
|
||||||
|
|
||||||
|
const selectorFn = type === 'chat' ? selectChat : type === 'user' ? selectUser : selectPeer;
|
||||||
|
|
||||||
|
return ids.filter((id) => {
|
||||||
|
const peer = selectorFn(global, id);
|
||||||
|
if (!peer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localizedTitle = isApiPeerChat(peer)
|
||||||
|
? getChatTitle(lang, peer)
|
||||||
|
: id === global.currentUserId ? lang('SavedMessages') : undefined;
|
||||||
|
const isFoundInLocalized = localizedTitle ? searchWords(localizedTitle) : undefined;
|
||||||
|
|
||||||
|
const name = getPeerFullTitle(lang, peer);
|
||||||
|
|
||||||
|
return isFoundInLocalized
|
||||||
|
|| (name && searchWords(name))
|
||||||
|
|| Boolean(peer.usernames?.find(({ username }) => searchWords(username)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getPeerTypeKey(peer: ApiPeer) {
|
export function getPeerTypeKey(peer: ApiPeer) {
|
||||||
if (isApiPeerChat(peer)) {
|
if (isApiPeerChat(peer)) {
|
||||||
if (peer.type === 'chatTypeBasicGroup' || peer.type === 'chatTypeSuperGroup') {
|
if (peer.type === 'chatTypeBasicGroup' || peer.type === 'chatTypeSuperGroup') {
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { formatFullDate, formatTime } from '../../util/dates/dateFormat';
|
|||||||
import { DAY } from '../../util/dates/units';
|
import { DAY } from '../../util/dates/units';
|
||||||
import { orderBy } from '../../util/iteratees';
|
import { orderBy } from '../../util/iteratees';
|
||||||
import { formatPhoneNumber } from '../../util/phoneNumber';
|
import { formatPhoneNumber } from '../../util/phoneNumber';
|
||||||
import { prepareSearchWordsForNeedle } from '../../util/searchWords';
|
|
||||||
import { getServerTime, getServerTimeOffset } from '../../util/serverTime';
|
import { getServerTime, getServerTimeOffset } from '../../util/serverTime';
|
||||||
|
|
||||||
export function getUserFirstOrLastName(user?: ApiUser) {
|
export function getUserFirstOrLastName(user?: ApiUser) {
|
||||||
@ -239,31 +238,6 @@ export function sortUserIds(
|
|||||||
}, 'desc');
|
}, 'desc');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterUsersByName(
|
|
||||||
userIds: string[],
|
|
||||||
usersById: Record<string, ApiUser>,
|
|
||||||
query?: string,
|
|
||||||
currentUserId?: string,
|
|
||||||
savedMessagesLang?: string,
|
|
||||||
) {
|
|
||||||
if (!query) {
|
|
||||||
return userIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchWords = prepareSearchWordsForNeedle(query);
|
|
||||||
|
|
||||||
return userIds.filter((id) => {
|
|
||||||
const user = usersById[id];
|
|
||||||
if (!user) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = id === currentUserId ? savedMessagesLang : getUserFullName(user);
|
|
||||||
|
|
||||||
return (name && searchWords(name)) || Boolean(user.usernames?.find(({ username }) => searchWords(username)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMainUsername(userOrChat: ApiPeer) {
|
export function getMainUsername(userOrChat: ApiPeer) {
|
||||||
return userOrChat.usernames?.find((u) => u.isActive)?.username;
|
return userOrChat.usernames?.find((u) => u.isActive)?.username;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,13 +14,18 @@ type ProjectActionTypes =
|
|||||||
type ProjectActionNames = keyof ProjectActionTypes;
|
type ProjectActionNames = keyof ProjectActionTypes;
|
||||||
|
|
||||||
type Helper<T, E> = Exclude<T, E> extends never ? {} : Exclude<T, E>;
|
type Helper<T, E> = Exclude<T, E> extends never ? {} : Exclude<T, E>;
|
||||||
|
|
||||||
|
export type TabStateActionNames = {
|
||||||
|
[ActionName in ProjectActionNames]:
|
||||||
|
'tabId' extends keyof Helper<ProjectActionTypes[ActionName], undefined> ? ActionName : never
|
||||||
|
}[ProjectActionNames];
|
||||||
// `Required` actions are called from actions to ensure the `tabId` is always provided if needed.
|
// `Required` actions are called from actions to ensure the `tabId` is always provided if needed.
|
||||||
// There are three types of actions:
|
// There are three types of actions:
|
||||||
// 1. With tabId, which is made required when calling action from another action handler
|
// 1. With tabId, which is made required when calling action from another action handler
|
||||||
// 2. Without payload (= undefined), hence made the payload not required
|
// 2. Without payload (= undefined), hence made the payload not required
|
||||||
// 3. With payload, hence made the payload required
|
// 3. With payload, hence made the payload required
|
||||||
export type RequiredGlobalActions = {
|
export type RequiredGlobalActions = {
|
||||||
[ActionName in ProjectActionNames]: 'tabId' extends keyof Helper<ProjectActionTypes[ActionName], undefined> ? ((
|
[ActionName in ProjectActionNames]: ActionName extends TabStateActionNames ? ((
|
||||||
payload: ProjectActionTypes[ActionName] & { tabId: number },
|
payload: ProjectActionTypes[ActionName] & { tabId: number },
|
||||||
options?: ActionOptions,
|
options?: ActionOptions,
|
||||||
) => void) :
|
) => void) :
|
||||||
|
|||||||
@ -3,9 +3,10 @@ import type { GlobalState, TabArgs } from '../types';
|
|||||||
|
|
||||||
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../config';
|
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../config';
|
||||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||||
|
import { isDeletedUser } from '../helpers';
|
||||||
import { selectChat, selectChatFullInfo } from './chats';
|
import { selectChat, selectChatFullInfo } from './chats';
|
||||||
import { selectTabState } from './tabs';
|
import { selectTabState } from './tabs';
|
||||||
import { selectBot, selectIsPremiumPurchaseBlocked, selectUser } from './users';
|
import { selectBot, selectUser } from './users';
|
||||||
|
|
||||||
export function selectPeer<T extends GlobalState>(global: T, peerId: string): ApiPeer | undefined {
|
export function selectPeer<T extends GlobalState>(global: T, peerId: string): ApiPeer | undefined {
|
||||||
return selectUser(global, peerId) || selectChat(global, peerId);
|
return selectUser(global, peerId) || selectChat(global, peerId);
|
||||||
@ -19,10 +20,11 @@ export function selectCanGift<T extends GlobalState>(global: T, peerId: string)
|
|||||||
const bot = selectBot(global, peerId);
|
const bot = selectBot(global, peerId);
|
||||||
const user = selectUser(global, peerId);
|
const user = selectUser(global, peerId);
|
||||||
|
|
||||||
const areStarGiftsAvailable = selectChatFullInfo(global, peerId)?.areStarGiftsAvailable || user;
|
if (user) {
|
||||||
|
return !bot && peerId !== SERVICE_NOTIFICATIONS_USER_ID && !isDeletedUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
return Boolean(!selectIsPremiumPurchaseBlocked(global) && !bot && peerId !== SERVICE_NOTIFICATIONS_USER_ID
|
return selectChatFullInfo(global, peerId)?.areStarGiftsAvailable;
|
||||||
&& areStarGiftsAvailable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectPeerSavedGifts<T extends GlobalState>(
|
export function selectPeerSavedGifts<T extends GlobalState>(
|
||||||
|
|||||||
@ -92,7 +92,7 @@ import type { WebApp, WebAppModalStateType, WebAppOutboundEvent } from '../../ty
|
|||||||
import type { DownloadableMedia } from '../helpers';
|
import type { DownloadableMedia } from '../helpers';
|
||||||
import type { TabState } from './tabState';
|
import type { TabState } from './tabState';
|
||||||
|
|
||||||
type WithTabId = { tabId?: number };
|
export type WithTabId = { tabId?: number };
|
||||||
|
|
||||||
export interface ActionPayloads {
|
export interface ActionPayloads {
|
||||||
// system
|
// system
|
||||||
@ -2308,6 +2308,7 @@ export interface ActionPayloads {
|
|||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closeGiftModal: WithTabId | undefined;
|
closeGiftModal: WithTabId | undefined;
|
||||||
sendStarGift: StarGiftInfo & WithTabId;
|
sendStarGift: StarGiftInfo & WithTabId;
|
||||||
|
|
||||||
openGiftInfoModalFromMessage: {
|
openGiftInfoModalFromMessage: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messageId: number;
|
messageId: number;
|
||||||
@ -2319,6 +2320,7 @@ export interface ActionPayloads {
|
|||||||
gift: ApiStarGift;
|
gift: ApiStarGift;
|
||||||
}) & WithTabId;
|
}) & WithTabId;
|
||||||
closeGiftInfoModal: WithTabId | undefined;
|
closeGiftInfoModal: WithTabId | undefined;
|
||||||
|
|
||||||
openGiftUpgradeModal: {
|
openGiftUpgradeModal: {
|
||||||
giftId: string;
|
giftId: string;
|
||||||
peerId?: string;
|
peerId?: string;
|
||||||
@ -2330,6 +2332,7 @@ export interface ActionPayloads {
|
|||||||
shouldKeepOriginalDetails?: boolean;
|
shouldKeepOriginalDetails?: boolean;
|
||||||
upgradeStars?: number;
|
upgradeStars?: number;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
|
||||||
openGiftWithdrawModal: {
|
openGiftWithdrawModal: {
|
||||||
gift: ApiSavedStarGift;
|
gift: ApiSavedStarGift;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
@ -2339,6 +2342,17 @@ export interface ActionPayloads {
|
|||||||
gift: ApiInputSavedStarGift;
|
gift: ApiInputSavedStarGift;
|
||||||
password: string;
|
password: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
|
||||||
|
openGiftTransferModal: {
|
||||||
|
gift: ApiSavedStarGift;
|
||||||
|
} & WithTabId;
|
||||||
|
transferGift: {
|
||||||
|
gift: ApiInputSavedStarGift;
|
||||||
|
transferStars?: number;
|
||||||
|
recipientId: string;
|
||||||
|
} & WithTabId;
|
||||||
|
closeGiftTransferModal: WithTabId | undefined;
|
||||||
|
|
||||||
loadPeerSavedGifts: {
|
loadPeerSavedGifts: {
|
||||||
peerId: string;
|
peerId: string;
|
||||||
shouldRefresh?: boolean;
|
shouldRefresh?: boolean;
|
||||||
|
|||||||
@ -722,6 +722,10 @@ export type TabState = {
|
|||||||
gift: ApiSavedStarGift | ApiStarGift;
|
gift: ApiSavedStarGift | ApiStarGift;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
giftTransferModal?: {
|
||||||
|
gift: ApiSavedStarGift;
|
||||||
|
};
|
||||||
|
|
||||||
giftUpgradeModal?: {
|
giftUpgradeModal?: {
|
||||||
sampleAttributes: ApiStarGiftAttribute[];
|
sampleAttributes: ApiStarGiftAttribute[];
|
||||||
recipientId?: string;
|
recipientId?: string;
|
||||||
@ -748,4 +752,5 @@ export type TabState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
isWaitingForStarGiftUpgrade?: true;
|
isWaitingForStarGiftUpgrade?: true;
|
||||||
|
isWaitingForStarGiftTransfer?: true;
|
||||||
};
|
};
|
||||||
|
|||||||
66
src/hooks/usePeerSearch.ts
Normal file
66
src/hooks/usePeerSearch.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { useState } from '../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { ApiChat } from '../api/types';
|
||||||
|
|
||||||
|
import { callApi } from '../api/gramjs';
|
||||||
|
import useAsync from './useAsync';
|
||||||
|
import useDebouncedMemo from './useDebouncedMemo';
|
||||||
|
import useLastCallback from './useLastCallback';
|
||||||
|
|
||||||
|
const DEBOUNCE_TIMEOUT = 300;
|
||||||
|
|
||||||
|
export async function peerGlobalSearch(query: string) {
|
||||||
|
const searchResult = await callApi('searchChats', { query });
|
||||||
|
if (!searchResult) return undefined;
|
||||||
|
|
||||||
|
const ids = [...searchResult.accountResultIds, ...searchResult.globalResultIds];
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prepareChatMemberSearch(chat: ApiChat) {
|
||||||
|
return async (query: string) => {
|
||||||
|
const searchResult = await callApi('fetchMembers', {
|
||||||
|
chat,
|
||||||
|
memberFilter: 'search',
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
|
||||||
|
return searchResult?.members?.map((member) => member.userId) || [];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function usePeerSearch({
|
||||||
|
query,
|
||||||
|
queryFn = peerGlobalSearch,
|
||||||
|
defaultValue,
|
||||||
|
debounceTimeout = DEBOUNCE_TIMEOUT,
|
||||||
|
isDisabled,
|
||||||
|
}: {
|
||||||
|
query: string;
|
||||||
|
queryFn?: (query: string) => Promise<string[] | undefined>;
|
||||||
|
defaultValue?: string[];
|
||||||
|
debounceTimeout?: number;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
}) {
|
||||||
|
const debouncedQuery = useDebouncedMemo(() => query, debounceTimeout, [query]);
|
||||||
|
const [currentResultsQuery, setCurrentResultsQuery] = useState<string>('');
|
||||||
|
const searchQuery = !query ? query : debouncedQuery; // Ignore debounce if query is empty
|
||||||
|
const queryCallback = useLastCallback(queryFn);
|
||||||
|
|
||||||
|
const result = useAsync(async () => {
|
||||||
|
if (!searchQuery || isDisabled) {
|
||||||
|
setCurrentResultsQuery('');
|
||||||
|
return Promise.resolve(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
const answer = await queryCallback(searchQuery);
|
||||||
|
setCurrentResultsQuery(searchQuery);
|
||||||
|
return answer;
|
||||||
|
}, [searchQuery, defaultValue, queryCallback, isDisabled], defaultValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
currentResultsQuery,
|
||||||
|
};
|
||||||
|
}
|
||||||
28
src/types/language.d.ts
vendored
28
src/types/language.d.ts
vendored
@ -1197,7 +1197,10 @@ export interface LangPair {
|
|||||||
'GiftInfoViewUpgraded': undefined;
|
'GiftInfoViewUpgraded': undefined;
|
||||||
'GiftInfoUpgradeBadge': undefined;
|
'GiftInfoUpgradeBadge': undefined;
|
||||||
'GiftInfoUpgradeForFree': undefined;
|
'GiftInfoUpgradeForFree': undefined;
|
||||||
'GiftInfoWithdraw': undefined;
|
'GiftInfoTransfer': undefined;
|
||||||
|
'GiftTransferTitle': undefined;
|
||||||
|
'GiftTransferTON': undefined;
|
||||||
|
'GiftTransferConfirmButtonFree': undefined;
|
||||||
'GiftUpgradeUniqueTitle': undefined;
|
'GiftUpgradeUniqueTitle': undefined;
|
||||||
'GiftUpgradeUniqueDescription': undefined;
|
'GiftUpgradeUniqueDescription': undefined;
|
||||||
'GiftUpgradeTransferableTitle': undefined;
|
'GiftUpgradeTransferableTitle': undefined;
|
||||||
@ -1672,6 +1675,10 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
|||||||
'GiftSend': {
|
'GiftSend': {
|
||||||
'amount': V;
|
'amount': V;
|
||||||
};
|
};
|
||||||
|
'GiftUnique': {
|
||||||
|
'title': V;
|
||||||
|
'number': V;
|
||||||
|
};
|
||||||
'GiftInfoPeerDescriptionFreeUpgradeOut': {
|
'GiftInfoPeerDescriptionFreeUpgradeOut': {
|
||||||
'peer': V;
|
'peer': V;
|
||||||
};
|
};
|
||||||
@ -1718,6 +1725,25 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
|||||||
'date': V;
|
'date': V;
|
||||||
'text': V;
|
'text': V;
|
||||||
};
|
};
|
||||||
|
'GiftTransferTONBlocked': {
|
||||||
|
'time': V;
|
||||||
|
};
|
||||||
|
'GiftTransferConfirmDescription': {
|
||||||
|
'gift': V;
|
||||||
|
'peer': V;
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'GiftTransferConfirmDescriptionFree': {
|
||||||
|
'gift': V;
|
||||||
|
'peer': V;
|
||||||
|
};
|
||||||
|
'GiftTransferConfirmButton': {
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'GiftTransferSuccessMessage': {
|
||||||
|
'gift': V;
|
||||||
|
'peer': V;
|
||||||
|
};
|
||||||
'GiftPeerUpgradeText': {
|
'GiftPeerUpgradeText': {
|
||||||
'peer': V;
|
'peer': V;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { TimeFormat } from '../../types';
|
|||||||
import type { LangFn } from '../localization';
|
import type { LangFn } from '../localization';
|
||||||
|
|
||||||
import withCache from '../withCache';
|
import withCache from '../withCache';
|
||||||
|
import { getDays } from './units';
|
||||||
|
|
||||||
const WEEKDAYS_FULL = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
const WEEKDAYS_FULL = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||||
const MONTHS_FULL = [
|
const MONTHS_FULL = [
|
||||||
@ -86,22 +87,26 @@ export function formatMonthAndYear(lang: OldLangFn, date: Date, isShort = false)
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function formatCountdown(
|
export function formatCountdown(
|
||||||
lang: OldLangFn,
|
lang: LangFn,
|
||||||
msLeft: number,
|
secondsLeft: number,
|
||||||
) {
|
) {
|
||||||
const days = Math.floor(msLeft / MILLISECONDS_IN_DAY);
|
const days = getDays(secondsLeft);
|
||||||
if (msLeft < 0) {
|
if (secondsLeft < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (days < 1) {
|
} else if (days < 1) {
|
||||||
return formatMediaDuration(msLeft / 1000);
|
return formatMediaDuration(secondsLeft);
|
||||||
} else if (days < 7) {
|
} else if (days < 7) {
|
||||||
return lang('Days', days);
|
const count = days;
|
||||||
|
return lang('Days', { count }, { pluralValue: count });
|
||||||
} else if (days < 30) {
|
} else if (days < 30) {
|
||||||
return lang('Weeks', Math.floor(days / 7));
|
const count = Math.floor(days / 7);
|
||||||
|
return lang('Weeks', { count }, { pluralValue: count });
|
||||||
} else if (days < 365) {
|
} else if (days < 365) {
|
||||||
return lang('Months', Math.floor(days / 30));
|
const count = Math.floor(days / 30);
|
||||||
|
return lang('Months', { count }, { pluralValue: count });
|
||||||
} else {
|
} else {
|
||||||
return lang('Years', Math.floor(days / 365));
|
const count = Math.floor(days / 365);
|
||||||
|
return lang('Years', { count }, { pluralValue: count });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,17 @@
|
|||||||
import { type FC, type Props, useRef } from '../../lib/teact/teact';
|
import { type FC, type Props, useRef } from '../../lib/teact/teact';
|
||||||
|
|
||||||
export default function freezeWhenClosed<T extends FC>(Component: T) {
|
type InjectProps<T extends FC, P extends Props> = FC<Parameters<T>[0] & P>;
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
ignoreFreeze?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function freezeWhenClosed<T extends FC>(Component: T): InjectProps<T, OwnProps> {
|
||||||
function ComponentWrapper(props: Props) {
|
function ComponentWrapper(props: Props) {
|
||||||
const newProps = useRef(props);
|
const newProps = useRef(props);
|
||||||
|
|
||||||
|
if (props.ignoreFreeze) return Component(props);
|
||||||
|
|
||||||
if (props.isOpen) {
|
if (props.isOpen) {
|
||||||
newProps.current = props;
|
newProps.current = props;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user