Support t.me/joinchat (#1035)

This commit is contained in:
Alexander Zinchuk 2021-04-20 18:19:58 +03:00
parent 851619349c
commit fc69c603a3
10 changed files with 59 additions and 11 deletions

View File

@ -885,6 +885,22 @@ export async function migrateChat(chat: ApiChat) {
return buildApiChatFromPreview(result.chats[1]); return buildApiChatFromPreview(result.chats[1]);
} }
export async function openChatByInvite(hash: string) {
const result = await invokeRequest(new GramJs.messages.CheckChatInvite({ hash }));
if (!result) {
return undefined;
}
if (result instanceof GramJs.ChatInvite) {
await invokeRequest(new GramJs.messages.ImportChatInvite({ hash }), true);
return undefined;
} else {
return buildApiChatFromPreview(result.chat);
}
}
function preparePeers( function preparePeers(
result: GramJs.messages.Dialogs | GramJs.messages.DialogsSlice | GramJs.messages.PeerDialogs, result: GramJs.messages.Dialogs | GramJs.messages.DialogsSlice | GramJs.messages.PeerDialogs,
) { ) {

View File

@ -12,7 +12,7 @@ export {
fetchChatFolders, editChatFolder, deleteChatFolder, fetchRecommendedChatFolders, fetchChatFolders, editChatFolder, deleteChatFolder, fetchRecommendedChatFolders,
getChatByUsername, togglePreHistoryHidden, updateChatDefaultBannedRights, updateChatMemberBannedRights, getChatByUsername, togglePreHistoryHidden, updateChatDefaultBannedRights, updateChatMemberBannedRights,
updateChatTitle, updateChatAbout, toggleSignatures, updateChatAdmin, fetchGroupsForDiscussion, setDiscussionGroup, updateChatTitle, updateChatAbout, toggleSignatures, updateChatAdmin, fetchGroupsForDiscussion, setDiscussionGroup,
migrateChat, migrateChat, openChatByInvite,
} from './chats'; } from './chats';
export { export {

View File

@ -3,7 +3,7 @@ import { withGlobal } from '../../lib/teact/teactn';
import convertPunycode from '../../lib/punycode'; import convertPunycode from '../../lib/punycode';
import { GlobalActions } from '../../global/types'; import { GlobalActions } from '../../global/types';
import { DEBUG, RE_TME_LINK } from '../../config'; import { DEBUG, RE_TME_INVITE_LINK, RE_TME_LINK } from '../../config';
import { pick } from '../../util/iteratees'; import { pick } from '../../util/iteratees';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
@ -26,7 +26,7 @@ const SafeLink: FC<OwnProps & DispatchProps> = ({
const handleClick = useCallback((e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => { const handleClick = useCallback((e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
if ( if (
e.ctrlKey || e.altKey || e.shiftKey || e.metaKey e.ctrlKey || e.altKey || e.shiftKey || e.metaKey
|| !url || !url.match(RE_TME_LINK) || !url || (!url.match(RE_TME_LINK) && !url.match(RE_TME_INVITE_LINK))
) { ) {
return true; return true;
} }

View File

@ -10,6 +10,7 @@ import { formatPastTimeShort } from '../../util/dateFormat';
import Media from './Media'; import Media from './Media';
import Link from '../ui/Link'; import Link from '../ui/Link';
import SafeLink from './SafeLink';
import './WebLink.scss'; import './WebLink.scss';
@ -75,9 +76,13 @@ const WebLink: FC<OwnProps> = ({ message, senderTitle, onMessageClick }) => {
{truncatedDescription && ( {truncatedDescription && (
<Link className="site-description" onClick={handleMessageClick}>{renderText(truncatedDescription)}</Link> <Link className="site-description" onClick={handleMessageClick}>{renderText(truncatedDescription)}</Link>
)} )}
<a href={url} target="_blank" rel="noopener noreferrer" className="site-name"> <SafeLink
url={url}
className="site-name"
text=""
>
{url.replace('mailto:', '') || displayUrl} {url.replace('mailto:', '') || displayUrl}
</a> </SafeLink>
{senderTitle && <div className="sender-name">{renderText(senderTitle)}</div>} {senderTitle && <div className="sender-name">{renderText(senderTitle)}</div>}
</div> </div>
{senderTitle && ( {senderTitle && (

View File

@ -106,6 +106,7 @@ export const CONTENT_TYPES_FOR_QUICK_UPLOAD = 'image/png,image/gif,image/jpeg,vi
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
export const RE_LINK_TEMPLATE = '((ftp|https?):\\/\\/)?((www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})\\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)'; export const RE_LINK_TEMPLATE = '((ftp|https?):\\/\\/)?((www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})\\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)';
export const RE_TME_LINK = /^(?:https?:\/\/)?(?:t\.me\/)([\d\w_]+)(?:\/([\d]+))?$/gm; export const RE_TME_LINK = /^(?:https?:\/\/)?(?:t\.me\/)([\d\w_]+)(?:\/([\d]+))?$/gm;
export const RE_TME_INVITE_LINK = /^(?:https?:\/\/)?(?:t\.me\/joinchat\/)([\d\w_]+)?$/gm;
// MTProto constants // MTProto constants
export const SERVICE_NOTIFICATIONS_USER_ID = 777000; export const SERVICE_NOTIFICATIONS_USER_ID = 777000;

View File

@ -901,6 +901,8 @@ messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers;
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite;
messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
messages.importChatInvite#6c50051c hash:string = Updates;
messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult; messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult;
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;

View File

@ -901,6 +901,8 @@ messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers;
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite;
messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
messages.importChatInvite#6c50051c hash:string = Updates;
messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult; messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult;
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;

View File

@ -2,7 +2,7 @@ import { addReducer, getDispatch } from '../../../lib/teact/teactn';
import { ApiChat } from '../../../api/types'; import { ApiChat } from '../../../api/types';
import { RE_TME_LINK } from '../../../config'; import { RE_TME_INVITE_LINK, RE_TME_LINK } from '../../../config';
import { callApi } from '../../../api/gramjs'; import { callApi } from '../../../api/gramjs';
import { selectChatMessage, selectCurrentChat } from '../../selectors'; import { selectChatMessage, selectCurrentChat } from '../../selectors';
@ -14,7 +14,7 @@ addReducer('clickInlineButton', (global, actions, payload) => {
actions.sendBotCommand({ command: button.value }); actions.sendBotCommand({ command: button.value });
break; break;
case 'url': case 'url':
if (button.value.match(RE_TME_LINK)) { if (button.value.match(RE_TME_INVITE_LINK) || button.value.match(RE_TME_LINK)) {
actions.openTelegramLink({ url: button.value }); actions.openTelegramLink({ url: button.value });
} else { } else {
window.open(button.value); window.open(button.value);

View File

@ -12,6 +12,7 @@ import {
ARCHIVED_FOLDER_ID, ARCHIVED_FOLDER_ID,
TOP_CHAT_MESSAGES_PRELOAD_LIMIT, TOP_CHAT_MESSAGES_PRELOAD_LIMIT,
CHAT_LIST_LOAD_SLICE, CHAT_LIST_LOAD_SLICE,
RE_TME_INVITE_LINK,
RE_TME_LINK, RE_TME_LINK,
TIPS_USERNAME, TIPS_USERNAME,
} from '../../../config'; } from '../../../config';
@ -380,12 +381,28 @@ addReducer('toggleChatUnread', (global, actions, payload) => {
addReducer('openTelegramLink', (global, actions, payload) => { addReducer('openTelegramLink', (global, actions, payload) => {
const { url } = payload!; const { url } = payload!;
const match = RE_TME_LINK.exec(url)!; let match = RE_TME_INVITE_LINK.exec(url);
const username = match[1]; if (match) {
const channelPostId = match[2] ? Number(match[2]) : undefined; const hash = match[1];
void openChatByUsername(actions, username, channelPostId); (async () => {
const chat = await callApi('openChatByInvite', hash);
if (!chat) {
return;
}
actions.openChat({ id: chat.id });
})();
} else {
match = RE_TME_LINK.exec(url)!;
const username = match[1];
const channelPostId = match[2] ? Number(match[2]) : undefined;
void openChatByUsername(actions, username, channelPostId);
}
}); });
addReducer('openChatByUsername', (global, actions, payload) => { addReducer('openChatByUsername', (global, actions, payload) => {

View File

@ -55,6 +55,11 @@ const READABLE_ERROR_MESSAGES: Record<string, string> = {
USER_KICKED: 'This user was kicked from this supergroup/channel', USER_KICKED: 'This user was kicked from this supergroup/channel',
USER_NOT_MUTUAL_CONTACT: 'The provided user is not a mutual contact', USER_NOT_MUTUAL_CONTACT: 'The provided user is not a mutual contact',
USER_PRIVACY_RESTRICTED: 'The user\'s privacy settings do not allow you to do this', USER_PRIVACY_RESTRICTED: 'The user\'s privacy settings do not allow you to do this',
INVITE_HASH_EMPTY: 'The invite hash is empty',
INVITE_HASH_EXPIRED: 'The invite link has expired',
INVITE_HASH_INVALID: 'The invite hash is invalid',
CHANNELS_TOO_MUCH: 'You have joined too many channels/supergroups',
USER_ALREADY_PARTICIPANT: 'You already in the group',
}; };
export default function getReadableErrorText(error: ApiError) { export default function getReadableErrorText(error: ApiError) {