Invite restricted users via link (#3982)
This commit is contained in:
parent
0e5660abd7
commit
4d70ae306d
@ -70,7 +70,7 @@ import {
|
||||
} from '../helpers';
|
||||
import localDb from '../localDb';
|
||||
import { scheduleMutedChatUpdate } from '../scheduleUnmute';
|
||||
import { applyState, updateChannelState } from '../updateManager';
|
||||
import { applyState, processUpdate, updateChannelState } from '../updateManager';
|
||||
import { invokeRequest, uploadFile } from './client';
|
||||
|
||||
type FullChatData = {
|
||||
@ -626,7 +626,7 @@ export async function createChannel({
|
||||
title, about = '', users,
|
||||
}: {
|
||||
title: string; about?: string; users?: ApiUser[];
|
||||
}, noErrorUpdate = false): Promise<ApiChat | undefined> {
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.channels.CreateChannel({
|
||||
broadcast: true,
|
||||
title,
|
||||
@ -657,20 +657,36 @@ export async function createChannel({
|
||||
|
||||
const channel = buildApiChatFromPreview(newChannel)!;
|
||||
|
||||
let restrictedUserIds: string[] | undefined;
|
||||
|
||||
if (users?.length) {
|
||||
try {
|
||||
await invokeRequest(new GramJs.channels.InviteToChannel({
|
||||
const updates = await invokeRequest(new GramJs.channels.InviteToChannel({
|
||||
channel: buildInputEntity(channel.id, channel.accessHash) as GramJs.InputChannel,
|
||||
users: users.map(({ id, accessHash }) => buildInputEntity(id, accessHash)) as GramJs.InputUser[],
|
||||
}), {
|
||||
shouldThrow: noErrorUpdate,
|
||||
shouldIgnoreUpdates: true,
|
||||
shouldThrow: true,
|
||||
});
|
||||
if (updates) {
|
||||
processUpdate(updates);
|
||||
restrictedUserIds = handleUserPrivacyRestrictedUpdates(updates);
|
||||
}
|
||||
} catch (err) {
|
||||
// `noErrorUpdate` will cause an exception which we don't want either
|
||||
if ((err as Error).message === 'USER_PRIVACY_RESTRICTED') {
|
||||
restrictedUserIds = users.map(({ id }) => id);
|
||||
} else {
|
||||
onUpdate({
|
||||
'@type': 'error',
|
||||
error: {
|
||||
message: (err as Error).message,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
return { channel, restrictedUserIds };
|
||||
}
|
||||
|
||||
export function joinChannel({
|
||||
@ -740,11 +756,12 @@ export async function createGroupChat({
|
||||
title, users,
|
||||
}: {
|
||||
title: string; users: ApiUser[];
|
||||
}): Promise<ApiChat | undefined> {
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.CreateChat({
|
||||
title,
|
||||
users: users.map(({ id, accessHash }) => buildInputEntity(id, accessHash)) as GramJs.InputUser[],
|
||||
}), {
|
||||
shouldIgnoreUpdates: true,
|
||||
shouldThrow: true,
|
||||
});
|
||||
|
||||
@ -758,6 +775,8 @@ export async function createGroupChat({
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
processUpdate(result);
|
||||
const restrictedUserIds = handleUserPrivacyRestrictedUpdates(result);
|
||||
|
||||
const newChat = result.chats[0];
|
||||
if (!newChat || !(newChat instanceof GramJs.Chat)) {
|
||||
@ -768,7 +787,7 @@ export async function createGroupChat({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return buildApiChatFromPreview(newChat);
|
||||
return { chat: buildApiChatFromPreview(newChat), restrictedUserIds };
|
||||
}
|
||||
|
||||
export async function editChatPhoto({
|
||||
@ -1265,31 +1284,64 @@ export async function openChatByInvite(hash: string) {
|
||||
return { chatId: chat.id };
|
||||
}
|
||||
|
||||
export async function addChatMembers(chat: ApiChat, users: ApiUser[], noErrorUpdate = false) {
|
||||
export async function addChatMembers(chat: ApiChat, users: ApiUser[]) {
|
||||
try {
|
||||
if (chat.type === 'chatTypeChannel' || chat.type === 'chatTypeSuperGroup') {
|
||||
return await invokeRequest(new GramJs.channels.InviteToChannel({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
users: users.map((user) => buildInputEntity(user.id, user.accessHash)) as GramJs.InputUser[],
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow: noErrorUpdate,
|
||||
});
|
||||
try {
|
||||
const updates = await invokeRequest(new GramJs.channels.InviteToChannel({
|
||||
channel: buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel,
|
||||
users: users.map((user) => buildInputEntity(user.id, user.accessHash)) as GramJs.InputUser[],
|
||||
}), {
|
||||
shouldIgnoreUpdates: true,
|
||||
shouldThrow: true,
|
||||
});
|
||||
if (updates) {
|
||||
processUpdate(updates);
|
||||
return handleUserPrivacyRestrictedUpdates(updates);
|
||||
}
|
||||
} catch (err) {
|
||||
if ((err as Error).message === 'USER_PRIVACY_RESTRICTED') {
|
||||
return users.map(({ id }) => id);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
return await Promise.all(users.map((user) => {
|
||||
return invokeRequest(new GramJs.messages.AddChatUser({
|
||||
chatId: buildInputEntity(chat.id) as BigInt.BigInteger,
|
||||
userId: buildInputEntity(user.id, user.accessHash) as GramJs.InputUser,
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
shouldThrow: noErrorUpdate,
|
||||
});
|
||||
}));
|
||||
const addChatUsersResult = await Promise.all(
|
||||
users.map(async (user) => {
|
||||
try {
|
||||
const updates = await invokeRequest(new GramJs.messages.AddChatUser({
|
||||
chatId: buildInputEntity(chat.id) as BigInt.BigInteger,
|
||||
userId: buildInputEntity(user.id, user.accessHash) as GramJs.InputUser,
|
||||
}), {
|
||||
shouldIgnoreUpdates: true,
|
||||
shouldThrow: true,
|
||||
});
|
||||
if (updates) {
|
||||
processUpdate(updates);
|
||||
return handleUserPrivacyRestrictedUpdates(updates);
|
||||
}
|
||||
return undefined;
|
||||
} catch (err) {
|
||||
if ((err as Error).message === 'USER_PRIVACY_RESTRICTED') {
|
||||
return [user.id];
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (addChatUsersResult) {
|
||||
return addChatUsersResult.flat().filter(Boolean);
|
||||
}
|
||||
} catch (err) {
|
||||
// `noErrorUpdate` will cause an exception which we don't want either
|
||||
return undefined;
|
||||
onUpdate({
|
||||
'@type': 'error',
|
||||
error: {
|
||||
message: (err as Error).message,
|
||||
},
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function deleteChatMember(chat: ApiChat, user: ApiUser) {
|
||||
@ -1819,3 +1871,24 @@ export function togglePeerTranslations({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
}));
|
||||
}
|
||||
|
||||
function handleUserPrivacyRestrictedUpdates(updates: GramJs.TypeUpdates) {
|
||||
if (!(updates instanceof GramJs.Updates) && !(updates instanceof GramJs.UpdatesCombined)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const eligibleUpdates = updates
|
||||
.updates
|
||||
.filter(
|
||||
(u): u is GramJs.UpdateGroupInvitePrivacyForbidden => {
|
||||
return u instanceof GramJs.UpdateGroupInvitePrivacyForbidden;
|
||||
},
|
||||
);
|
||||
|
||||
if (eligibleUpdates.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return eligibleUpdates
|
||||
.map((u) => buildApiPeerId(u.userId, 'user'));
|
||||
}
|
||||
|
||||
@ -674,6 +674,11 @@ export type ApiUpdateNewAuthorization = {
|
||||
location?: string;
|
||||
};
|
||||
|
||||
export type ApiUpdateGroupInvitePrivacyForbidden = {
|
||||
'@type': 'updateGroupInvitePrivacyForbidden';
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export type ApiUpdate = (
|
||||
ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate |
|
||||
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
||||
@ -702,7 +707,7 @@ export type ApiUpdate = (
|
||||
ApiUpdatePinnedTopicsOrder | ApiUpdateTopic | ApiUpdateTopics | ApiUpdateRecentEmojiStatuses |
|
||||
ApiUpdateRecentReactions | ApiUpdateStory | ApiUpdateReadStories | ApiUpdateDeleteStory | ApiUpdateSentStoryReaction |
|
||||
ApiRequestReconnectApi | ApiRequestSync | ApiUpdateFetchingDifference | ApiUpdateChannelMessages |
|
||||
ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization
|
||||
ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization | ApiUpdateGroupInvitePrivacyForbidden
|
||||
);
|
||||
|
||||
export type OnApiUpdate = (update: ApiUpdate) => void;
|
||||
|
||||
@ -81,3 +81,4 @@ export { default as Management } from '../components/right/management/Management
|
||||
|
||||
export { default as PaymentModal } from '../components/payment/PaymentModal';
|
||||
export { default as ReceiptModal } from '../components/payment/ReceiptModal';
|
||||
export { default as InviteViaLinkModal } from '../components/main/InviteViaLinkModal';
|
||||
|
||||
18
src/components/main/InviteViaLinkModal.async.tsx
Normal file
18
src/components/main/InviteViaLinkModal.async.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React from '../../lib/teact/teact';
|
||||
|
||||
import type { OwnProps } from './InviteViaLinkModal';
|
||||
|
||||
import { Bundles } from '../../util/moduleLoader';
|
||||
|
||||
import useModuleLoader from '../../hooks/useModuleLoader';
|
||||
|
||||
const InviteViaLinkModalAsync: FC<OwnProps> = (props) => {
|
||||
const { userIds, chatId } = props;
|
||||
const InviteViaLinkModal = useModuleLoader(Bundles.Extra, 'InviteViaLinkModal', !(userIds && chatId));
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return InviteViaLinkModal ? <InviteViaLinkModal {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default InviteViaLinkModalAsync;
|
||||
3
src/components/main/InviteViaLinkModal.module.scss
Normal file
3
src/components/main/InviteViaLinkModal.module.scss
Normal file
@ -0,0 +1,3 @@
|
||||
.contentText {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
89
src/components/main/InviteViaLinkModal.tsx
Normal file
89
src/components/main/InviteViaLinkModal.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback,
|
||||
useEffect,
|
||||
useMemo, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, getGlobal } from '../../global';
|
||||
|
||||
import { getUserFullName } from '../../global/helpers';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
|
||||
import Picker from '../common/Picker';
|
||||
import Button from '../ui/Button';
|
||||
import Modal from '../ui/Modal';
|
||||
|
||||
import styles from './InviteViaLinkModal.module.scss';
|
||||
|
||||
export type OwnProps = {
|
||||
chatId?: string;
|
||||
userIds?: string[];
|
||||
};
|
||||
|
||||
const InviteViaLinkModal: FC<OwnProps> = ({
|
||||
chatId, userIds,
|
||||
}) => {
|
||||
const { sendInviteMessages, closeInviteViaLinkModal } = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
const [selectedMemberIds, setSelectedMemberIds] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userIds) {
|
||||
setSelectedMemberIds(userIds);
|
||||
}
|
||||
}, [userIds]);
|
||||
|
||||
const handleClose = useLastCallback(() => closeInviteViaLinkModal());
|
||||
const handleClickSkip = useLastCallback(() => closeInviteViaLinkModal());
|
||||
|
||||
const handleClickSendInviteLink = useCallback(() => {
|
||||
sendInviteMessages({ chatId: chatId!, userIds: selectedMemberIds! });
|
||||
closeInviteViaLinkModal();
|
||||
}, [selectedMemberIds, chatId]);
|
||||
|
||||
const userNames = useMemo(() => {
|
||||
const usersById = getGlobal().users.byId;
|
||||
return userIds?.map((userId) => getUserFullName(usersById[userId])).join(', ');
|
||||
}, [userIds]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={Boolean(userIds && chatId)}
|
||||
title={lang('SendInviteLink.InviteTitle')}
|
||||
onClose={handleClose}
|
||||
isSlim
|
||||
>
|
||||
<p className={styles.contentText}>
|
||||
{renderText(lang('SendInviteLink.TextAvailableSingleUser', userNames), ['simple_markdown'])}
|
||||
</p>
|
||||
<Picker
|
||||
itemIds={userIds!}
|
||||
selectedIds={selectedMemberIds ?? []}
|
||||
onSelectedIdsChange={setSelectedMemberIds}
|
||||
/>
|
||||
<div className="dialog-buttons">
|
||||
<Button
|
||||
className="confirm-dialog-button"
|
||||
isText
|
||||
onClick={handleClickSendInviteLink}
|
||||
disabled={!selectedMemberIds?.length}
|
||||
>
|
||||
{lang('SendInviteLink.ActionInvite')}
|
||||
</Button>
|
||||
<Button
|
||||
className="confirm-dialog-button"
|
||||
isText
|
||||
onClick={handleClickSkip}
|
||||
>
|
||||
{lang('SendInviteLink.ActionSkip')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(InviteViaLinkModal);
|
||||
@ -96,6 +96,7 @@ import DraftRecipientPicker from './DraftRecipientPicker.async';
|
||||
import ForwardRecipientPicker from './ForwardRecipientPicker.async';
|
||||
import GameModal from './GameModal';
|
||||
import HistoryCalendar from './HistoryCalendar.async';
|
||||
import InviteViaLinkModal from './InviteViaLinkModal.async';
|
||||
import NewContactModal from './NewContactModal.async';
|
||||
import Notifications from './Notifications.async';
|
||||
import PremiumLimitReachedModal from './premium/common/PremiumLimitReachedModal.async';
|
||||
@ -161,6 +162,7 @@ type StateProps = {
|
||||
noRightColumnAnimation?: boolean;
|
||||
withInterfaceAnimations?: boolean;
|
||||
isSynced?: boolean;
|
||||
inviteViaLinkModal?: TabState['inviteViaLinkModal'];
|
||||
};
|
||||
|
||||
const APP_OUTDATED_TIMEOUT_MS = 5 * 60 * 1000; // 5 min
|
||||
@ -222,6 +224,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
boostModal,
|
||||
noRightColumnAnimation,
|
||||
isSynced,
|
||||
inviteViaLinkModal,
|
||||
}) => {
|
||||
const {
|
||||
initMain,
|
||||
@ -586,6 +589,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
||||
<ReceiptModal isOpen={isReceiptModalOpen} onClose={clearReceipt} />
|
||||
<DeleteFolderDialog folder={deleteFolderDialog} />
|
||||
<ReactionPicker isOpen={isReactionPickerOpen} />
|
||||
<InviteViaLinkModal userIds={inviteViaLinkModal?.restrictedUserIds} chatId={inviteViaLinkModal?.chatId} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -629,6 +633,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
chatlistModal,
|
||||
boostModal,
|
||||
giftCodeModal,
|
||||
inviteViaLinkModal,
|
||||
} = selectTabState(global);
|
||||
|
||||
const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer;
|
||||
@ -696,6 +701,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
giftCodeModal,
|
||||
noRightColumnAnimation,
|
||||
isSynced: global.isSynced,
|
||||
inviteViaLinkModal,
|
||||
};
|
||||
},
|
||||
)(Main));
|
||||
|
||||
@ -53,6 +53,7 @@ import {
|
||||
addMessages,
|
||||
addUsers,
|
||||
addUserStatuses,
|
||||
addUsersToRestrictedInviteList,
|
||||
deleteTopic,
|
||||
leaveChat,
|
||||
replaceChatFullInfo,
|
||||
@ -436,9 +437,11 @@ addActionHandler('createChannel', async (global, actions, payload): Promise<void
|
||||
setGlobal(global);
|
||||
|
||||
let createdChannel: ApiChat | undefined;
|
||||
|
||||
let restrictedUserIds: string[] | undefined;
|
||||
try {
|
||||
createdChannel = await callApi('createChannel', { title, about, users });
|
||||
const result = await callApi('createChannel', { title, about, users });
|
||||
createdChannel = result?.channel;
|
||||
restrictedUserIds = result?.restrictedUserIds;
|
||||
} catch (error) {
|
||||
global = getGlobal();
|
||||
|
||||
@ -474,6 +477,12 @@ addActionHandler('createChannel', async (global, actions, payload): Promise<void
|
||||
setGlobal(global);
|
||||
actions.openChat({ id: channelId, shouldReplaceHistory: true, tabId });
|
||||
|
||||
if (restrictedUserIds) {
|
||||
global = getGlobal();
|
||||
global = addUsersToRestrictedInviteList(global, restrictedUserIds, channelId, tabId);
|
||||
setGlobal(global);
|
||||
}
|
||||
|
||||
if (channelId && accessHash && photo) {
|
||||
await callApi('editChatPhoto', { chatId: channelId, accessHash, photo });
|
||||
}
|
||||
@ -593,17 +602,19 @@ addActionHandler('createGroupChat', async (global, actions, payload): Promise<vo
|
||||
}, tabId);
|
||||
setGlobal(global);
|
||||
|
||||
let createdChatId: string | undefined;
|
||||
try {
|
||||
const createdChat = await callApi('createGroupChat', {
|
||||
const { chat: createdChat, restrictedUserIds } = await callApi('createGroupChat', {
|
||||
title,
|
||||
users,
|
||||
});
|
||||
}) ?? {};
|
||||
|
||||
if (!createdChat) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { id: chatId } = createdChat;
|
||||
createdChatId = chatId;
|
||||
|
||||
global = getGlobal();
|
||||
global = updateChat(global, chatId, createdChat);
|
||||
@ -619,6 +630,11 @@ addActionHandler('createGroupChat', async (global, actions, payload): Promise<vo
|
||||
shouldReplaceHistory: true,
|
||||
tabId,
|
||||
});
|
||||
if (restrictedUserIds) {
|
||||
global = getGlobal();
|
||||
global = addUsersToRestrictedInviteList(global, restrictedUserIds, chatId, tabId);
|
||||
setGlobal(global);
|
||||
}
|
||||
|
||||
if (chatId && photo) {
|
||||
await callApi('editChatPhoto', {
|
||||
@ -626,8 +642,8 @@ addActionHandler('createGroupChat', async (global, actions, payload): Promise<vo
|
||||
photo,
|
||||
});
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.message === 'USERS_TOO_FEW') {
|
||||
} catch (err) {
|
||||
if ((err as ApiError).message === 'USERS_TOO_FEW') {
|
||||
global = getGlobal();
|
||||
global = updateTabState(global, {
|
||||
chatCreation: {
|
||||
@ -637,6 +653,10 @@ addActionHandler('createGroupChat', async (global, actions, payload): Promise<vo
|
||||
},
|
||||
}, tabId);
|
||||
setGlobal(global);
|
||||
} else if ((err as ApiError).message === 'USER_PRIVACY_RESTRICTED') {
|
||||
global = getGlobal();
|
||||
global = addUsersToRestrictedInviteList(global, users.map(({ id }) => id), createdChatId!, tabId);
|
||||
setGlobal(global);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1645,7 +1665,12 @@ addActionHandler('addChatMembers', async (global, actions, payload): Promise<voi
|
||||
}
|
||||
|
||||
actions.setNewChatMembersDialogState({ newChatMembersProgress: NewChatMembersProgress.Loading, tabId });
|
||||
await callApi('addChatMembers', chat, users);
|
||||
const restrictedUserIds = await callApi('addChatMembers', chat, users);
|
||||
if (restrictedUserIds) {
|
||||
global = getGlobal();
|
||||
global = addUsersToRestrictedInviteList(global, restrictedUserIds, chat.id, tabId);
|
||||
setGlobal(global);
|
||||
}
|
||||
actions.setNewChatMembersDialogState({ newChatMembersProgress: NewChatMembersProgress.Closed, tabId });
|
||||
global = getGlobal();
|
||||
loadFullChat(global, actions, chat, tabId);
|
||||
|
||||
@ -79,6 +79,7 @@ import {
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import {
|
||||
selectChat,
|
||||
selectChatFullInfo,
|
||||
selectChatMessage,
|
||||
selectCurrentChat,
|
||||
selectCurrentMessageList,
|
||||
@ -360,6 +361,33 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
|
||||
return undefined;
|
||||
});
|
||||
|
||||
addActionHandler('sendInviteMessages', async (global, actions, payload): Promise<void> => {
|
||||
const { chatId, userIds, tabId = getCurrentTabId() } = payload;
|
||||
const chatFullInfo = selectChatFullInfo(global, chatId);
|
||||
if (!chatFullInfo?.inviteLink) {
|
||||
return undefined;
|
||||
}
|
||||
const userFullNames: string[] = [];
|
||||
await Promise.all(userIds.map((userId) => {
|
||||
const chat = selectChat(global, userId);
|
||||
if (!chat) {
|
||||
return undefined;
|
||||
}
|
||||
const userFullName = getUserFullName(selectUser(global, userId));
|
||||
if (userFullName) {
|
||||
userFullNames.push(userFullName);
|
||||
}
|
||||
return sendMessage(global, {
|
||||
chat,
|
||||
text: chatFullInfo.inviteLink,
|
||||
});
|
||||
}));
|
||||
return actions.showNotification({
|
||||
message: translate('Conversation.ShareLinkTooltip.Chat.One', userFullNames.join(', ')),
|
||||
tabId,
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('editMessage', (global, actions, payload): ActionReturnType => {
|
||||
const {
|
||||
messageList, text, entities, tabId = getCurrentTabId(),
|
||||
|
||||
@ -723,6 +723,13 @@ addActionHandler('updatePageTitle', (global, actions, payload): ActionReturnType
|
||||
setPageTitleInstant(IS_ELECTRON ? '' : PAGE_TITLE);
|
||||
});
|
||||
|
||||
addActionHandler('closeInviteViaLinkModal', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload ?? {};
|
||||
return updateTabState(global, {
|
||||
inviteViaLinkModal: undefined,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
let prevIsScreenLocked: boolean | undefined;
|
||||
let prevBlurredTabsCount: number = 0;
|
||||
let onlineTimeout: number | undefined;
|
||||
|
||||
@ -3,7 +3,7 @@ import type { GlobalState, TabArgs, TabState } from '../types';
|
||||
|
||||
import { areDeepEqual } from '../../util/areDeepEqual';
|
||||
import { getCurrentTabId } from '../../util/establishMultitabRole';
|
||||
import { omit, pick } from '../../util/iteratees';
|
||||
import { omit, pick, unique } from '../../util/iteratees';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
||||
import { selectTabState } from '../selectors';
|
||||
import { updateChat } from './chats';
|
||||
@ -263,3 +263,19 @@ export function closeNewContactDialog<T extends GlobalState>(
|
||||
newContact: undefined,
|
||||
}, tabId);
|
||||
}
|
||||
|
||||
export function addUsersToRestrictedInviteList<T extends GlobalState>(
|
||||
global: T,
|
||||
userIds: string[],
|
||||
chatId: string,
|
||||
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||
): T {
|
||||
const { inviteViaLinkModal } = selectTabState(global, tabId);
|
||||
return updateTabState(global, {
|
||||
inviteViaLinkModal: {
|
||||
...inviteViaLinkModal,
|
||||
restrictedUserIds: unique([...inviteViaLinkModal?.restrictedUserIds ?? [], ...userIds]),
|
||||
chatId,
|
||||
},
|
||||
}, tabId);
|
||||
}
|
||||
|
||||
@ -641,6 +641,11 @@ export type TabState = {
|
||||
slug: string;
|
||||
info: ApiCheckedGiftCode;
|
||||
};
|
||||
|
||||
inviteViaLinkModal?: {
|
||||
restrictedUserIds: string[];
|
||||
chatId: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type GlobalState = {
|
||||
@ -1278,6 +1283,10 @@ export interface ActionPayloads {
|
||||
messageList?: MessageList;
|
||||
isReaction?: true; // Reaction to the story are sent in the form of a message
|
||||
} & WithTabId;
|
||||
sendInviteMessages: {
|
||||
chatId: string;
|
||||
userIds: string[];
|
||||
} & WithTabId;
|
||||
cancelSendingMessage: {
|
||||
chatId: string;
|
||||
messageId: number;
|
||||
@ -2578,6 +2587,7 @@ export interface ActionPayloads {
|
||||
dismissNotification: { localId: string } & WithTabId;
|
||||
|
||||
updatePageTitle: WithTabId | undefined;
|
||||
closeInviteViaLinkModal: WithTabId | undefined;
|
||||
|
||||
// Calls
|
||||
joinGroupCall: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user