From 1f1cb9c48933fc85497701a18ebf9897c28cf39e Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Mon, 9 Jan 2023 17:44:17 +0400 Subject: [PATCH] Full seamless login support --- src/components/chat/bubbles.ts | 83 +--------- .../sidebarLeft/tabs/activeSessions.ts | 5 +- .../sidebarLeft/tabs/activeWebSessions.ts | 114 ++++++++++++++ .../sidebarLeft/tabs/privacyAndSecurity.ts | 54 ++++++- src/config/app.ts | 2 +- src/helpers/addAnchorListener.ts | 5 +- src/helpers/dom/cancelEvent.ts | 4 +- src/lang.ts | 12 ++ src/lib/appManagers/appImManager.ts | 149 +++++++++++++++++- src/lib/appManagers/appMessagesManager.ts | 27 ---- .../appManagers/appSeamlessLoginManager.ts | 55 +++++++ src/lib/appManagers/createManagers.ts | 4 +- src/lib/appManagers/manager.ts | 2 + src/lib/mtproto/apiManager.ts | 7 + src/scss/partials/_avatar.scss | 5 + src/scss/partials/_button.scss | 2 +- 16 files changed, 403 insertions(+), 127 deletions(-) create mode 100644 src/components/sidebarLeft/tabs/activeWebSessions.ts create mode 100644 src/lib/appManagers/appSeamlessLoginManager.ts diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 3ff729bf1..91ba5b4be 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -3982,92 +3982,15 @@ export default class ChatBubbles { const {mid} = message; const {url, button_id} = button; - const openWindow = (url: string) => { - window.open(url, '_blank'); - }; - - const onUrlAuthResultAccepted = (urlAuthResult: UrlAuthResult.urlAuthResultAccepted) => { - openWindow(urlAuthResult.url); - }; - - const onUrlAuthResult = async(urlAuthResult: UrlAuthResult): Promise => { - if(urlAuthResult._ === 'urlAuthResultRequest') { - const b = document.createElement('b'); - b.append(urlAuthResult.domain); - const peerTitle = await wrapPeerTitle({peerId: rootScope.myId}); - const botPeerTitle = await wrapPeerTitle({peerId: urlAuthResult.bot.id.toPeerId()}); - - const logInCheckbox: PopupPeerCheckboxOptions = { - text: 'OpenUrlOption1', - textArgs: [b.cloneNode(true), peerTitle], - checked: true - }; - - const allowMessagesCheckbox: PopupPeerCheckboxOptions = urlAuthResult.pFlags.request_write_access ? { - text: 'OpenUrlOption2', - textArgs: [botPeerTitle], - checked: true - } : undefined; - - const checkboxes: PopupPeerCheckboxOptions[] = [ - logInCheckbox, - allowMessagesCheckbox - ]; - - const confirmationPromise = confirmationPopup({ - titleLangKey: 'OpenUrlTitle', - button: { - langKey: 'Open' - }, - descriptionLangKey: 'OpenUrlAlert2', - descriptionLangArgs: [b], - checkboxes: checkboxes.filter(Boolean) - }); - - if(allowMessagesCheckbox) { - logInCheckbox.checkboxField.input.addEventListener('change', () => { - const disabled = !logInCheckbox.checkboxField.checked; - allowMessagesCheckbox.checkboxField.toggleDisability(disabled); - - if(disabled) { - allowMessagesCheckbox.checkboxField.checked = false; - } - }); - } - - const [logInChecked, allowMessagesChecked] = await confirmationPromise; - - if(!logInChecked) { - openWindow(url); - return; - } - - const result = await this.managers.appMessagesManager.acceptUrlAuth( - peerId, - mid, - url, - button_id, - allowMessagesChecked - ); - - return onUrlAuthResult(result); - } else if(urlAuthResult._ === 'urlAuthResultAccepted') { - onUrlAuthResultAccepted(urlAuthResult); - } else { - openWindow(url); - } - }; - attachClickEvent(buttonEl, () => { const toggle = toggleDisability([buttonEl], true); - this.managers.appMessagesManager.requestUrlAuth( + this.chat.appImManager.handleUrlAuth({ peerId, mid, url, - button_id - ).then((urlAuthResult) => { + buttonId: button_id + }).then(() => { toggle(); - onUrlAuthResult(urlAuthResult); }); }); break; diff --git a/src/components/sidebarLeft/tabs/activeSessions.ts b/src/components/sidebarLeft/tabs/activeSessions.ts index dd216b116..45d3f5b0f 100644 --- a/src/components/sidebarLeft/tabs/activeSessions.ts +++ b/src/components/sidebarLeft/tabs/activeSessions.ts @@ -167,10 +167,7 @@ export default class AppActiveSessionsTab extends SliderSuperTabEventable { } onCloseAfterTimeout() { - if(this.menuElement) { - this.menuElement.remove(); - } - + this.menuElement?.remove(); return super.onCloseAfterTimeout(); } } diff --git a/src/components/sidebarLeft/tabs/activeWebSessions.ts b/src/components/sidebarLeft/tabs/activeWebSessions.ts new file mode 100644 index 000000000..6ca7d2c3d --- /dev/null +++ b/src/components/sidebarLeft/tabs/activeWebSessions.ts @@ -0,0 +1,114 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import {formatDateAccordingToTodayNew} from '../../../helpers/date'; +import {attachClickEvent} from '../../../helpers/dom/clickEvent'; +import findUpClassName from '../../../helpers/dom/findUpClassName'; +import toggleDisability from '../../../helpers/dom/toggleDisability'; +import {WebAuthorization} from '../../../layer'; +import AvatarElement from '../../avatar'; +import Button from '../../button'; +import confirmationPopup from '../../confirmationPopup'; +import Row from '../../row'; +import SettingSection from '../../settingSection'; +import {SliderSuperTabEventable} from '../../sliderTab'; +import wrapPeerTitle from '../../wrappers/peerTitle'; + +export default class AppActiveWebSessionsTab extends SliderSuperTabEventable { + public async init(sessions: WebAuthorization[]) { + this.container.classList.add('active-sessions-container'); + this.setTitle('WebSessionsTitle'); + + const Session = async(auth: WebAuthorization) => { + const peerId = auth.bot_id.toPeerId(); + const row = new Row({ + title: await wrapPeerTitle({peerId}), + subtitle: [auth.ip, auth.region].join(' - '), + clickable: true, + titleRight: formatDateAccordingToTodayNew(new Date(Math.max(auth.date_active, auth.date_created) * 1000)) + }); + + const media = row.createMedia('big'); + const avatar = new AvatarElement(); + avatar.classList.add('avatar-48'); + await avatar.updateWithOptions({peerId}); + media.append(avatar); + + row.container.dataset.hash = '' + auth.hash; + row.container.dataset.peerId = '' + peerId; + + row.midtitle.textContent = [auth.domain, auth.browser, auth.platform].filter(Boolean).join(', '); + + return row; + }; + + { + const section = new SettingSection({ + caption: 'ClearOtherWebSessionsHelp' + }); + + const btnTerminate = Button('btn-primary btn-transparent danger', {icon: 'stop', text: 'TerminateAllWebSessions'}); + + attachClickEvent(btnTerminate, async() => { + await confirmationPopup({ + descriptionLangKey: 'AreYouSureWebSessions', + button: { + langKey: 'Disconnect', + isDanger: true + } + }); + + const toggle = toggleDisability([btnTerminate], true); + this.managers.appSeamlessLoginManager.resetWebAuthorizations().then(() => { + this.close(); + }); + }, {listenerSetter: this.listenerSetter}); + + section.content.append(btnTerminate); + + this.scrollable.append(section.container); + } + + { + const section = new SettingSection({ + name: 'OtherWebSessions', + caption: 'TerminateWebSessionInfo' + }); + + const rows = await Promise.all(sessions.map(Session)); + section.content.append(...rows.map((row) => row.container)); + + let leftLength = rows.length; + attachClickEvent(section.content, async(e) => { + const row = findUpClassName(e.target, 'row'); + if(!row) { + return; + } + + await confirmationPopup({ + descriptionLangKey: 'TerminateWebSessionText', + descriptionLangArgs: [await wrapPeerTitle({peerId: row.dataset.peerId.toPeerId()})], + button: { + langKey: 'Disconnect', + isDanger: true + } + }); + + const hash = row.dataset.hash; + row.classList.add('is-disabled'); + this.managers.appSeamlessLoginManager.resetWebAuthorization(hash).then(() => { + if(!--leftLength) { + this.close(); + } else { + row.remove(); + } + }); + }, {listenerSetter: this.listenerSetter}); + + this.scrollable.append(section.container); + } + } +} diff --git a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts index fcda1cf6b..04bb68ac2 100644 --- a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts +++ b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts @@ -6,7 +6,7 @@ import SliderSuperTab, {SliderSuperTabEventable} from '../../sliderTab'; import Row from '../../row'; -import {AccountPassword, Authorization, InputPrivacyKey, Updates} from '../../../layer'; +import {AccountPassword, Authorization, InputPrivacyKey, Updates, WebAuthorization} from '../../../layer'; import AppPrivacyPhoneNumberTab from './privacy/phoneNumber'; import AppTwoStepVerificationTab from './2fa'; import AppTwoStepVerificationEnterPasswordTab from './2fa/enterPassword'; @@ -33,16 +33,21 @@ import noop from '../../../helpers/noop'; import {toastNew} from '../../toast'; import AppPrivacyVoicesTab from './privacy/voices'; import SettingSection from '../../settingSection'; +import AppActiveWebSessionsTab from './activeWebSessions'; export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { private activeSessionsRow: Row; private authorizations: Authorization.authorization[]; + private websitesRow: Row; + private websites: WebAuthorization[]; + public static getInitArgs(fromTab: SliderSuperTab) { return { appConfig: fromTab.managers.apiManager.getAppConfig(), globalPrivacy: fromTab.managers.appPrivacyManager.getGlobalPrivacySettings(), - contentSettings: fromTab.managers.apiManager.invokeApi('account.getContentSettings') + contentSettings: fromTab.managers.apiManager.invokeApi('account.getContentSettings'), + webAuthorizations: fromTab.managers.appSeamlessLoginManager.getWebAuthorizations() }; } @@ -51,6 +56,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { this.setTitle('PrivacySettings'); const SUBTITLE: LangPackKey = 'Loading'; + const promises: Promise[] = []; { const section = new SettingSection({noDelimiter: true, caption: 'SessionsInfo'}); @@ -113,7 +119,22 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { }); activeSessionsRow.freezed = true; - section.content.append(blockedUsersRow.container, twoFactorRow.container, activeSessionsRow.container); + const websitesRow = this.websitesRow = new Row({ + icon: 'mention', + titleLangKey: 'OtherWebSessions', + subtitleLangKey: SUBTITLE, + clickable: () => { + const tab = this.slider.createTab(AppActiveWebSessionsTab); + tab.eventListener.addEventListener('destroy', () => { + this.updateActiveWebsites(); + }); + tab.open(this.websites); + }, + listenerSetter: this.listenerSetter + }); + websitesRow.freezed = true; + + section.content.append(blockedUsersRow.container, twoFactorRow.container, activeSessionsRow.container, websitesRow.container); this.scrollable.append(section.container); const setBlockedCount = (count: number) => { @@ -152,6 +173,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { }); this.updateActiveSessions(); + promises.push(this.updateActiveWebsites(p.webAuthorizations)); } { @@ -271,8 +293,6 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { }); } - const promises: Promise[] = []; - { const section = new SettingSection({name: 'NewChatsFromNonContacts', caption: 'ArchiveAndMuteInfo'}); @@ -441,15 +461,35 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { this.scrollable.append(section.container); } + // { + // const section = new SettingSection({ + // name: 'OtherWebSessions' + // }); + + // const row = new Row({ + + // }); + + // this.scrollable.append(section.container); + // } + return Promise.all(promises); } public updateActiveSessions() { - this.managers.apiManager.invokeApi('account.getAuthorizations').then((auths) => { + return this.managers.apiManager.invokeApi('account.getAuthorizations').then((auths) => { this.activeSessionsRow.freezed = false; this.authorizations = auths.authorizations; _i18n(this.activeSessionsRow.subtitle, 'Privacy.Devices', [this.authorizations.length]); - // console.log('auths', auths); + }); + } + + public updateActiveWebsites(promise = this.managers.appSeamlessLoginManager.getWebAuthorizations()) { + return promise.then((authorizations) => { + this.websitesRow.freezed = false; + this.websites = authorizations; + _i18n(this.websitesRow.subtitle, 'Privacy.Websites', [this.websites.length]); + this.websitesRow.container.classList.toggle('hide', !this.websites.length); }); } } diff --git a/src/config/app.ts b/src/config/app.ts index cc88695f5..861011e1c 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -21,7 +21,7 @@ const App = { version: process.env.VERSION, versionFull: process.env.VERSION_FULL, build: +process.env.BUILD, - langPackVersion: '0.7.5', + langPackVersion: '0.7.6', langPack: 'webk', langPackCode: 'en', domains: MAIN_DOMAINS, diff --git a/src/helpers/addAnchorListener.ts b/src/helpers/addAnchorListener.ts index 6830b7d40..5b8d1044d 100644 --- a/src/helpers/addAnchorListener.ts +++ b/src/helpers/addAnchorListener.ts @@ -14,10 +14,11 @@ export default function addAnchorListener boolean | any, noPathnameParams?: boolean, - noUriParams?: boolean + noUriParams?: boolean, + noCancelEvent?: boolean }) { (window as any)[(options.protocol ? options.protocol + '_' : '') + options.name] = (element?: HTMLAnchorElement/* , e: Event */) => { - cancelEvent(null); + !options.noCancelEvent && cancelEvent(null); let href = element.href; let pathnameParams: any[]; diff --git a/src/helpers/dom/cancelEvent.ts b/src/helpers/dom/cancelEvent.ts index ccc58effe..90e41cad5 100644 --- a/src/helpers/dom/cancelEvent.ts +++ b/src/helpers/dom/cancelEvent.ts @@ -9,8 +9,8 @@ * https://github.com/zhukov/webogram/blob/master/LICENSE */ -export default function cancelEvent(event: Event) { - event = event || window.event; +export default function cancelEvent(event?: Event) { + event ||= window.event; if(event) { // @ts-ignore event = event.originalEvent || event; diff --git a/src/lang.ts b/src/lang.ts index fad668c99..ea9a4ef95 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -98,6 +98,10 @@ const lang = { 'one_value': '%1$d device', 'other_value': '%1$d devices' }, + 'Privacy.Websites': { + 'one_value': '%1$d website', + 'other_value': '%1$d websites' + }, 'Privacy.SensitiveContent': 'Sensitive Content', 'PrivacyModal.Search.Placeholder': 'Add Users or Groups...', 'Permissions.NoExceptions': 'No exceptions', @@ -855,6 +859,14 @@ const lang = { 'EditTopicHideInfo': 'If the \'General\' topic is hidden, group members can pull down in the topic list to view it.', 'OpenUrlOption1': 'Log in to %1$s as **%2$s**', 'OpenUrlOption2': 'Allow **%1$s** to send me messages', + 'OtherWebSessions': 'Connected websites', + 'WebSessionsTitle': 'Logged In with Telegram', + 'TerminateWebSessionText': 'Are you sure you want to disconnect %1$s?', + 'Disconnect': 'Disconnect', + 'TerminateAllWebSessions': 'Disconnect All Websites', + 'AreYouSureWebSessions': 'Are you sure you want to disconnect all websites where you logged in with Telegram?', + 'ClearOtherWebSessionsHelp': 'You can log in on websites that support signing in with Telegram.', + 'TerminateWebSessionInfo': 'Tap to disconnect from your Telegram account.', // * macos 'AccountSettings.Filters': 'Chat Folders', diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 3fc1ee6aa..a784ddf5b 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -27,9 +27,9 @@ import {MOUNT_CLASS_TO} from '../../config/debug'; import appNavigationController from '../../components/appNavigationController'; import AppPrivateSearchTab from '../../components/sidebarRight/tabs/search'; import I18n, {i18n, join, LangPackKey} from '../langPack'; -import {ChatFull, ChatInvite, ChatParticipant, ChatParticipants, Message, MessageAction, MessageMedia, SendMessageAction, User, Chat as MTChat} from '../../layer'; +import {ChatFull, ChatInvite, ChatParticipant, ChatParticipants, Message, MessageAction, MessageMedia, SendMessageAction, User, Chat as MTChat, UrlAuthResult} from '../../layer'; import PeerTitle from '../../components/peerTitle'; -import PopupPeer from '../../components/popups/peer'; +import PopupPeer, {PopupPeerCheckboxOptions} from '../../components/popups/peer'; import blurActiveElement from '../../helpers/dom/blurActiveElement'; import cancelEvent from '../../helpers/dom/cancelEvent'; import disableTransition from '../../helpers/dom/disableTransition'; @@ -98,6 +98,8 @@ import {MiddleEllipsisElement} from '../../components/middleEllipsis'; import addAnchorListener from '../../helpers/addAnchorListener'; import parseUriParams from '../../helpers/string/parseUriParams'; import getMessageThreadId from './utils/messages/getMessageThreadId'; +import findUpTag from '../../helpers/dom/findUpTag'; +import {MTAppConfig} from '../mtproto/appConfig'; export type ChatSavedPosition = { mids: number[], @@ -783,6 +785,149 @@ export class AppImManager extends EventListenerBase<{ this.onHashChange(true); this.attachKeydownListener(); + this.handleAutologinDomains(); + } + + public handleUrlAuth(options: { + peerId?: PeerId, + mid?: number, + buttonId?: number, + url: string + }) { + const {peerId, mid, buttonId, url} = options; + + const openWindow = (url: string) => { + window.open(url, '_blank'); + }; + + const onUrlAuthResultAccepted = (urlAuthResult: UrlAuthResult.urlAuthResultAccepted) => { + openWindow(urlAuthResult.url); + }; + + const onUrlAuthResult = async(urlAuthResult: UrlAuthResult): Promise => { + if(urlAuthResult._ === 'urlAuthResultRequest') { + const b = document.createElement('b'); + b.append(urlAuthResult.domain); + const peerTitle = await wrapPeerTitle({peerId: rootScope.myId}); + const botPeerTitle = await wrapPeerTitle({peerId: urlAuthResult.bot.id.toPeerId()}); + + const logInCheckbox: PopupPeerCheckboxOptions = { + text: 'OpenUrlOption1', + textArgs: [b.cloneNode(true), peerTitle], + checked: true + }; + + const allowMessagesCheckbox: PopupPeerCheckboxOptions = urlAuthResult.pFlags.request_write_access ? { + text: 'OpenUrlOption2', + textArgs: [botPeerTitle], + checked: true + } : undefined; + + const checkboxes: PopupPeerCheckboxOptions[] = [ + logInCheckbox, + allowMessagesCheckbox + ]; + + const confirmationPromise = confirmationPopup({ + titleLangKey: 'OpenUrlTitle', + button: { + langKey: 'Open' + }, + descriptionLangKey: 'OpenUrlAlert2', + descriptionLangArgs: [b], + checkboxes: checkboxes.filter(Boolean) + }); + + if(allowMessagesCheckbox) { + logInCheckbox.checkboxField.input.addEventListener('change', () => { + const disabled = !logInCheckbox.checkboxField.checked; + allowMessagesCheckbox.checkboxField.toggleDisability(disabled); + + if(disabled) { + allowMessagesCheckbox.checkboxField.checked = false; + } + }); + } + + const [logInChecked, allowMessagesChecked] = await confirmationPromise; + + if(!logInChecked) { + openWindow(url); + return; + } + + const result = await this.managers.appSeamlessLoginManager.acceptUrlAuth( + url, + peerId, + mid, + buttonId, + allowMessagesChecked + ); + + return onUrlAuthResult(result); + } else if(urlAuthResult._ === 'urlAuthResultAccepted') { + onUrlAuthResultAccepted(urlAuthResult); + } else { + openWindow(url); + } + }; + + return this.managers.appSeamlessLoginManager.requestUrlAuth( + url, + peerId, + mid, + buttonId + ).then((urlAuthResult) => { + onUrlAuthResult(urlAuthResult); + }); + } + + private handleAutologinDomains() { + let appConfig: MTAppConfig; + rootScope.addEventListener('app_config', (_appConfig) => { + appConfig = _appConfig; + }); + + const onAnchorClick = (element: HTMLAnchorElement) => { + const url = new URL(element.href); + if(appConfig.url_auth_domains.includes(url.hostname)) { + this.handleUrlAuth({url: element.href}); + cancelEvent(); + return; + } + + if(!appConfig.autologin_token || !appConfig.autologin_domains) { + return; + } + + const originalUrl = element.dataset.originalUrl ??= element.href; + if(appConfig.autologin_domains.includes(url.hostname)) { + url.searchParams.set('autologin_token', appConfig.autologin_token); + element.href = url.toString(); + + setTimeout(() => { + element.href = originalUrl; + delete element.dataset.originalUrl; + }, 0); + } + }; + + document.addEventListener('click', (e) => { + const anchor = findUpTag(e.target as HTMLElement, 'A') as HTMLAnchorElement; + if(anchor?.href) { + onAnchorClick(anchor); + } + }); + + // addAnchorListener({ + // name: 'handleUrlClick', + // callback: (_, element) => { + // onAnchorClick(element); + // }, + // noCancelEvent: true, + // noPathnameParams: true, + // noUriParams: true + // }); } private getStackFromElement(element: HTMLElement) { diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index f2adf849e..366e2db24 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -6399,31 +6399,4 @@ export class AppMessagesManager extends AppManager { send_as: this.appPeersManager.getInputPeerById(sendAsPeerId) }); } - - public requestUrlAuth(peerId: PeerId, mid: number, url: string, buttonId: number) { - return this.apiManager.invokeApi('messages.requestUrlAuth', { - button_id: buttonId, - msg_id: getServerMessageId(mid), - peer: this.appPeersManager.getInputPeerById(peerId), - url - }).then((urlAuthResult) => { - if(urlAuthResult._ === 'urlAuthResultRequest') { - this.appUsersManager.saveApiUser(urlAuthResult.bot); - } - - return urlAuthResult; - }); - } - - public acceptUrlAuth(peerId: PeerId, mid: number, url: string, buttonId: number, writeAllowed?: boolean) { - return this.apiManager.invokeApi('messages.acceptUrlAuth', { - button_id: buttonId, - msg_id: getServerMessageId(mid), - peer: this.appPeersManager.getInputPeerById(peerId), - url, - write_allowed: writeAllowed - }).then((urlAuthResult) => { - return urlAuthResult as Exclude; - }); - } } diff --git a/src/lib/appManagers/appSeamlessLoginManager.ts b/src/lib/appManagers/appSeamlessLoginManager.ts new file mode 100644 index 000000000..bd8a3649c --- /dev/null +++ b/src/lib/appManagers/appSeamlessLoginManager.ts @@ -0,0 +1,55 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import {UrlAuthResult} from '../../layer'; +import {AppManager} from './manager'; +import getServerMessageId from './utils/messageId/getServerMessageId'; + +export default class AppSeamlessLoginManager extends AppManager { + public requestUrlAuth(url: string, peerId?: PeerId, mid?: number, buttonId?: number) { + return this.apiManager.invokeApi('messages.requestUrlAuth', { + button_id: buttonId, + msg_id: mid ? getServerMessageId(mid) : undefined, + peer: peerId ? this.appPeersManager.getInputPeerById(peerId) : undefined, + url + }).then((urlAuthResult) => { + if(urlAuthResult._ === 'urlAuthResultRequest') { + this.appUsersManager.saveApiUser(urlAuthResult.bot); + } + + return urlAuthResult; + }); + } + + public acceptUrlAuth(url: string, peerId?: PeerId, mid?: number, buttonId?: number, writeAllowed?: boolean) { + return this.apiManager.invokeApi('messages.acceptUrlAuth', { + button_id: buttonId, + msg_id: mid ? getServerMessageId(mid) : undefined, + peer: peerId ? this.appPeersManager.getInputPeerById(peerId) : undefined, + url, + write_allowed: writeAllowed + }).then((urlAuthResult) => { + return urlAuthResult as Exclude; + }); + } + + public getWebAuthorizations() { + return this.apiManager.invokeApi('account.getWebAuthorizations').then((webAuthorizations) => { + this.appUsersManager.saveApiUsers(webAuthorizations.users); + return webAuthorizations.authorizations; + }); + } + + public resetWebAuthorization(hash: Long) { + return this.apiManager.invokeApi('account.resetWebAuthorization', { + hash + }); + } + + public resetWebAuthorizations() { + return this.apiManager.invokeApi('account.resetWebAuthorizations'); + } +} diff --git a/src/lib/appManagers/createManagers.ts b/src/lib/appManagers/createManagers.ts index f30665af4..a5ee27b8e 100644 --- a/src/lib/appManagers/createManagers.ts +++ b/src/lib/appManagers/createManagers.ts @@ -46,6 +46,7 @@ import filterUnique from '../../helpers/array/filterUnique'; import AppWebDocsManager from './appWebDocsManager'; import AppPaymentsManager from './appPaymentsManager'; import AppAttachMenuBotsManager from './appAttachMenuBotsManager'; +import AppSeamlessLoginManager from './appSeamlessLoginManager'; export default function createManagers(appStoragesManager: AppStoragesManager, userId: UserId) { const managers = { @@ -88,7 +89,8 @@ export default function createManagers(appStoragesManager: AppStoragesManager, u appStateManager: appStateManager, appWebDocsManager: new AppWebDocsManager, appPaymentsManager: new AppPaymentsManager, - appAttachMenuBotsManager: new AppAttachMenuBotsManager + appAttachMenuBotsManager: new AppAttachMenuBotsManager, + appSeamlessLoginManager: new AppSeamlessLoginManager }; type T = typeof managers; diff --git a/src/lib/appManagers/manager.ts b/src/lib/appManagers/manager.ts index 8dbd69e30..d6d8c3557 100644 --- a/src/lib/appManagers/manager.ts +++ b/src/lib/appManagers/manager.ts @@ -44,6 +44,7 @@ import type {AppUsersManager} from './appUsersManager'; import type AppWebDocsManager from './appWebDocsManager'; import type {AppWebPagesManager} from './appWebPagesManager'; import type AppAttachMenuBotsManager from './appAttachMenuBotsManager'; +import type AppSeamlessLoginManager from './appSeamlessLoginManager'; import type {AppManagers} from './managers'; export class AppManager { @@ -88,6 +89,7 @@ export class AppManager { protected appWebDocsManager: AppWebDocsManager; protected appPaymentsManager: AppPaymentsManager; protected appAttachMenuBotsManager: AppAttachMenuBotsManager; + protected appSeamlessLoginManager: AppSeamlessLoginManager; public clear: (init?: boolean) => void; diff --git a/src/lib/mtproto/apiManager.ts b/src/lib/mtproto/apiManager.ts index 8f89696ae..733108d15 100644 --- a/src/lib/mtproto/apiManager.ts +++ b/src/lib/mtproto/apiManager.ts @@ -98,6 +98,13 @@ export class ApiManager extends ApiManagerMethods { this.changeTransportType(transportType); }); // #endif + + // * Make sure that the used autologin_token is no more than 10000 seconds old + // * https://core.telegram.org/api/url-authorization + const REFRESH_APP_CONFIG_INTERVAL = (10000 - 30) * 1000; + setInterval(() => { + this.getAppConfig(true); + }, REFRESH_APP_CONFIG_INTERVAL); } protected after() { diff --git a/src/scss/partials/_avatar.scss b/src/scss/partials/_avatar.scss index 8d593e40e..70418cfd5 100644 --- a/src/scss/partials/_avatar.scss +++ b/src/scss/partials/_avatar.scss @@ -225,6 +225,11 @@ avatar-element { --multiplier: 1.35; } + &.avatar-36 { + --size: 36px; + --multiplier: 1.5; + } + &.avatar-35 { --size: 35px; --multiplier: 1.542857; diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index 6583f66e1..997614b0c 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -114,7 +114,7 @@ $btn-menu-z-index: 4; font-size: 1rem; backdrop-filter: var(--menu-backdrop-filter); min-width: 11.25rem; - max-width: 16.25rem; + // max-width: 16.25rem; &-old { padding: .5rem 0;