From 5145a28e42e376ea1524502116dffe777eaa6722 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Tue, 29 Jun 2021 17:21:16 +0300 Subject: [PATCH] Handle t.me/joinchat --- src/components/popups/createPoll.ts | 1 - src/components/popups/joinChatInvite.ts | 80 +++++++++++ src/lang.ts | 2 + src/lib/appManagers/appChatsManager.ts | 4 +- src/lib/appManagers/appImManager.ts | 131 ++++++++++++------ src/scss/partials/_avatar.scss | 4 + src/scss/partials/popups/_joinChatInvite.scss | 20 +++ src/scss/style.scss | 1 + 8 files changed, 196 insertions(+), 47 deletions(-) create mode 100644 src/components/popups/joinChatInvite.ts create mode 100644 src/scss/partials/popups/_joinChatInvite.scss diff --git a/src/components/popups/createPoll.ts b/src/components/popups/createPoll.ts index 07aa803f..bb79b5df 100644 --- a/src/components/popups/createPoll.ts +++ b/src/components/popups/createPoll.ts @@ -12,7 +12,6 @@ import InputField from "../inputField"; import RadioField from "../radioField"; import Scrollable from "../scrollable"; import SendContextMenu from "../chat/sendContextMenu"; -import { MessageEntity } from "../../layer"; import I18n, { _i18n, i18n } from "../../lib/langPack"; import findUpTag from "../../helpers/dom/findUpTag"; import { cancelEvent } from "../../helpers/dom/cancelEvent"; diff --git a/src/components/popups/joinChatInvite.ts b/src/components/popups/joinChatInvite.ts new file mode 100644 index 00000000..331d1ceb --- /dev/null +++ b/src/components/popups/joinChatInvite.ts @@ -0,0 +1,80 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import PopupElement, { addCancelButton } from "."; +import { ChatInvite, Updates } from "../../layer"; +import apiUpdatesManager from "../../lib/appManagers/apiUpdatesManager"; +import appAvatarsManager from "../../lib/appManagers/appAvatarsManager"; +import appPhotosManager from "../../lib/appManagers/appPhotosManager"; +import { i18n } from "../../lib/langPack"; +import apiManager from "../../lib/mtproto/mtprotoworker"; +import RichTextProcessor from "../../lib/richtextprocessor"; +import rootScope from "../../lib/rootScope"; +import AvatarElement from "../avatar"; +import { wrapPhoto } from "../wrappers"; + +// const FAKE_CHAT_ID = Number.MAX_SAFE_INTEGER - 0x1000; + +export default class PopupJoinChatInvite extends PopupElement { + constructor(hash: string, chatInvite: ChatInvite.chatInvite) { + super('popup-join-chat-invite', addCancelButton([{ + langKey: chatInvite.pFlags.broadcast ? 'JoinByPeekChannelTitle' : 'JoinByPeekGroupTitle', + callback: () => { + apiManager.invokeApi('messages.importChatInvite', {hash}) + .then((updates) => { + apiUpdatesManager.processUpdateMessage(updates); + const chat = (updates as Updates.updates).chats[0]; + const peerId = -chat.id; + rootScope.dispatchEvent('history_focus', {peerId}); + }); + } + }]), {closable: true, overlayClosable: true, body: true}); + + this.header.remove(); + + /* const fakeChat: Chat.channel | Chat.chat = { + _: chatInvite.pFlags.channel ? 'channel' : 'chat', + id: FAKE_CHAT_ID, + title: chatInvite.title, + photo: chatInvite.photo as any, + date: Date.now() / 1000 | 0, + version: 0, + participants_count: chatInvite.participants_count, + pFlags: chatInvite.pFlags as any + }; + + appChatsManager.saveApiChat(fakeChat); */ + + const avatarElem = new AvatarElement(); + avatarElem.setAttribute('dialog', '0'); + avatarElem.classList.add('avatar-100'); + if(chatInvite.photo._ === 'photo') { + chatInvite.photo = appPhotosManager.savePhoto(chatInvite.photo); + wrapPhoto({ + container: avatarElem, + message: null, + photo: chatInvite.photo, + boxHeight: 100, + boxWidth: 100, + withoutPreloader: true + }); + avatarElem.style.width = avatarElem.style.height = ''; + } else { + appAvatarsManager.putPhoto(avatarElem, -0, false, chatInvite.title); + } + + const title = document.createElement('div'); + title.classList.add('chat-title'); + title.innerHTML = RichTextProcessor.wrapEmojiText(chatInvite.title); + //avatarElem.setAttribute('peer', '' + -fakeChat.id); + + const isBroadcast = chatInvite.pFlags.broadcast; + const peopleCount = i18n(isBroadcast ? 'Subscribers' : 'Members', [chatInvite.participants_count]); + peopleCount.classList.add('chat-participants-count'); + + this.body.append(avatarElem, title, peopleCount); + } +} diff --git a/src/lang.ts b/src/lang.ts index 2231f88b..246c217e 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -480,6 +480,8 @@ const lang = { }, "TodayAtFormattedWithToday": "today at %1$s", "formatDateAtTime": "%1$s at %2$s", + "JoinByPeekChannelTitle": "Join Channel", + "JoinByPeekGroupTitle": "Join Group", // * macos "AccountSettings.Filters": "Chat Folders", diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 54fe05c9..b4d902fe 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -457,10 +457,10 @@ export class AppChatsManager { broadcast: true, title, about - }).then((updates: any) => { + }).then((updates) => { apiUpdatesManager.processUpdateMessage(updates); - const channelId = updates.chats[0].id; + const channelId = (updates as any).chats[0].id; rootScope.dispatchEvent('history_focus', {peerId: -channelId}); return channelId; diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 5b84e9b8..91af975c 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -43,7 +43,7 @@ import appNavigationController from '../../components/appNavigationController'; import appNotificationsManager from './appNotificationsManager'; import AppPrivateSearchTab from '../../components/sidebarRight/tabs/search'; import { i18n, LangPackKey } from '../langPack'; -import { SendMessageAction } from '../../layer'; +import { ChatInvite, SendMessageAction } from '../../layer'; import { hslaStringToHex } from '../../helpers/color'; import { copy, getObjectKeysAndSort } from '../../helpers/object'; import { getFilesFromEvent } from '../../helpers/files'; @@ -60,6 +60,7 @@ import appEmojiManager from './appEmojiManager'; import PopupElement from '../../components/popups'; import singleInstance from '../mtproto/singleInstance'; import PopupStickers from '../../components/popups/stickers'; +import PopupJoinChatInvite from '../../components/popups/joinChatInvite'; //console.log('appImManager included33!'); @@ -224,66 +225,108 @@ export class AppImManager { stateStorage.setToCache('chatPositions', c || {}); }); - this.addAnchorListener('showMaskedAlert', (params, element) => { - const href = element.href; + this.addAnchorListener({ + name: 'showMaskedAlert', + callback: (params, element) => { + const href = element.href; + + const a = element.cloneNode(true) as HTMLAnchorElement; + a.className = 'anchor-url'; + a.innerText = href; + a.removeAttribute('onclick'); + + new PopupPeer('popup-masked-url', { + titleLangKey: 'OpenUrlTitle', + descriptionLangKey: 'OpenUrlAlert2', + descriptionLangArgs: [a], + buttons: [{ + langKey: 'Open', + callback: () => { + a.click(); + }, + }] + }).show(); + }, + noParams: true + }); - const a = element.cloneNode(true) as HTMLAnchorElement; - a.className = 'anchor-url'; - a.innerText = href; - a.removeAttribute('onclick'); - - new PopupPeer('popup-masked-url', { - titleLangKey: 'OpenUrlTitle', - descriptionLangKey: 'OpenUrlAlert2', - descriptionLangArgs: [a], - buttons: [{ - langKey: 'Open', - callback: () => { - a.click(); - }, - }] - }).show(); - }, false); - - this.addAnchorListener('execBotCommand', (params) => { - const {command, bot} = params; - - /* const promise = bot ? this.openUsername(bot).then(() => this.chat.peerId) : Promise.resolve(this.chat.peerId); - promise.then(peerId => { - appMessagesManager.sendText(peerId, '/' + command); - }); */ + this.addAnchorListener<{command: string, bot: string}>({ + name: 'execBotCommand', + callback: (params) => { + const {command, bot} = params; - appMessagesManager.sendText(this.chat.peerId, '/' + command + (bot ? '@' + bot : '')); + /* const promise = bot ? this.openUsername(bot).then(() => this.chat.peerId) : Promise.resolve(this.chat.peerId); + promise.then(peerId => { + appMessagesManager.sendText(peerId, '/' + command); + }); */ - //console.log(command, bot); + appMessagesManager.sendText(this.chat.peerId, '/' + command + (bot ? '@' + bot : '')); + + //console.log(command, bot); + } }); - this.addAnchorListener('searchByHashtag', (params) => { - if(!params) { - return; + this.addAnchorListener<{hashtag: string}>({ + name: 'searchByHashtag', + callback: (params) => { + if(!params) { + return; + } + + const {hashtag} = params; + this.chat.initSearch('#' + hashtag + ' '); } + }); - const {hashtag} = params; - this.chat.initSearch('#' + hashtag + ' '); + this.addAnchorListener<['addstickers', string]>({ + name: 'addstickers', + callback: (params) => { + new PopupStickers({id: params[1]}).show(); + }, + parsePathname: true }); - this.addAnchorListener('addstickers', (params) => { - new PopupStickers({id: params[1]}).show(); - }, true); + this.addAnchorListener<['joinchat', string]>({ + name: 'joinchat', + callback: (params) => { + apiManager.invokeApi('messages.checkChatInvite', { + hash: params[1] + }).then(chatInvite => { + if((chatInvite as ChatInvite.chatInvitePeek).chat) { + appChatsManager.saveApiChat((chatInvite as ChatInvite.chatInvitePeek).chat, true); + } + + // console.log(chatInvite); + + if(chatInvite._ === 'chatInviteAlready' || + chatInvite._ === 'chatInvitePeek'/* && chatInvite.expires > tsNow(true) */) { + this.setInnerPeer(-chatInvite.chat.id); + return; + } + + new PopupJoinChatInvite(params[1], chatInvite).show(); + }); + }, + parsePathname: true + }); } - private addAnchorListener(name: 'showMaskedAlert' | 'execBotCommand' | 'searchByHashtag' | 'addstickers', - callback: (params: any, element: HTMLAnchorElement) => boolean | void, parseParams = true) { - (window as any)[name] = (element: HTMLAnchorElement, e: Event) => { + private addAnchorListener(options: { + name: 'showMaskedAlert' | 'execBotCommand' | 'searchByHashtag' | 'addstickers' | 'joinchat', + callback: (params: Params, element: HTMLAnchorElement) => boolean | void, + noParams?: boolean, + parsePathname?: boolean + }) { + (window as any)[options.name] = (element: HTMLAnchorElement/* , e: Event */) => { cancelEvent(null); const href = element.href; let params: any; - if(parseParams) { - params = !element.href.includes('#') ? new URL(element.href).pathname.split('/').slice(1) : this.parseUriParams(href); + if(!options.noParams) { + params = options.parsePathname ? new URL(element.href).pathname.split('/').slice(1) : this.parseUriParams(href); } - const res = callback(params, element); + const res = options.callback(params, element); return res === undefined ? res : false; }; } diff --git a/src/scss/partials/_avatar.scss b/src/scss/partials/_avatar.scss index 93bbfd26..a2b9d09e 100644 --- a/src/scss/partials/_avatar.scss +++ b/src/scss/partials/_avatar.scss @@ -80,6 +80,10 @@ avatar-element { &:before { line-height: inherit !important; } + + &.media-container { + position: relative; + } img { //width: 100% !important; diff --git a/src/scss/partials/popups/_joinChatInvite.scss b/src/scss/partials/popups/_joinChatInvite.scss new file mode 100644 index 00000000..07e0d201 --- /dev/null +++ b/src/scss/partials/popups/_joinChatInvite.scss @@ -0,0 +1,20 @@ +.popup-join-chat-invite { + user-select: none; + + .popup-body { + align-items: center; + padding: .5rem 0 1.25rem; + } + + .chat-title { + font-weight: bold; + margin: .75rem 0 .25rem; + line-height: var(--line-height); + } + + .chat-participants-count { + color: var(--secondary-text-color); + font-size: .875rem; + line-height: var(--line-height); + } +} \ No newline at end of file diff --git a/src/scss/style.scss b/src/scss/style.scss index 93b9d6d0..356fa734 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -272,6 +272,7 @@ html.night { @import "partials/popups/createPoll"; @import "partials/popups/forward"; @import "partials/popups/instanceDeactivated"; +@import "partials/popups/joinChatInvite"; @import "partials/pages/pages"; @import "partials/pages/authCode";