From ac313c94bd7e5e3e75d857abd561c43a50efe284 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Mon, 1 Mar 2021 23:49:36 +0400 Subject: [PATCH] Active sessions functions --- src/components/chat/contextMenu.ts | 1 + src/components/dialogsContextMenu.ts | 1 + src/components/popups/index.ts | 20 +-- .../sidebarLeft/tabs/activeSessions.ts | 161 +++++++++++++----- .../sidebarLeft/tabs/privacyAndSecurity.ts | 22 ++- src/index.hbs | 7 - src/scss/partials/_button.scss | 3 - src/scss/partials/_leftSidebar.scss | 6 + src/scss/style.scss | 13 +- 9 files changed, 164 insertions(+), 70 deletions(-) diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index 90b5423c..ca331a27 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -292,6 +292,7 @@ export default class ChatContextMenu { this.element = ButtonMenu(this.buttons, this.chat.bubbles.listenerSetter); this.element.id = 'bubble-contextmenu'; + this.element.classList.add('contextmenu'); this.chat.container.append(this.element); }; diff --git a/src/components/dialogsContextMenu.ts b/src/components/dialogsContextMenu.ts index 69f2f188..654df0ac 100644 --- a/src/components/dialogsContextMenu.ts +++ b/src/components/dialogsContextMenu.ts @@ -83,6 +83,7 @@ export default class DialogsContextMenu { this.element = ButtonMenu(this.buttons); this.element.id = 'dialogs-contextmenu'; + this.element.classList.add('contextmenu'); document.getElementById('page-chats').append(this.element); } diff --git a/src/components/popups/index.ts b/src/components/popups/index.ts index c75c8e63..1577706e 100644 --- a/src/components/popups/index.ts +++ b/src/components/popups/index.ts @@ -50,17 +50,17 @@ export default class PopupElement { this.header.prepend(this.btnClose); this.btnClose.addEventListener('click', this.hide, {once: true}); + } - if(options.overlayClosable) { - const onOverlayClick = (e: MouseEvent) => { - if(!findUpClassName(e.target, 'popup-container')) { - this.btnClose.click(); - this.element.removeEventListener('click', onOverlayClick); - } - }; - - this.element.addEventListener('click', onOverlayClick); - } + if(options.overlayClosable) { + const onOverlayClick = (e: MouseEvent) => { + if(!findUpClassName(e.target, 'popup-container')) { + this.hide(); + this.element.removeEventListener('click', onOverlayClick); + } + }; + + this.element.addEventListener('click', onOverlayClick); } if(options.withConfirm) { diff --git a/src/components/sidebarLeft/tabs/activeSessions.ts b/src/components/sidebarLeft/tabs/activeSessions.ts index 77defaf5..654e5871 100644 --- a/src/components/sidebarLeft/tabs/activeSessions.ts +++ b/src/components/sidebarLeft/tabs/activeSessions.ts @@ -2,75 +2,158 @@ import { SliderSuperTab } from "../../slider"; import { SettingSection } from ".."; import Button from "../../button"; import Row from "../../row"; +import { Authorization } from "../../../layer"; +import { formatDateAccordingToToday } from "../../../helpers/date"; +import { attachContextMenuListener, openBtnMenu, positionMenu } from "../../misc"; +import { attachClickEvent, findUpClassName, toggleDisability } from "../../../helpers/dom"; +import ButtonMenu from "../../buttonMenu"; +import PopupConfirmAction from "../../popups/confirmAction"; +import apiManager from "../../../lib/mtproto/mtprotoworker"; +import { toast } from "../../toast"; export default class AppActiveSessionsTab extends SliderSuperTab { + public authorizations: Authorization.authorization[]; + private menuElement: HTMLElement; + protected init() { this.container.classList.add('active-sessions-container'); this.title.innerText = 'Active Sessions'; - const Session = (options: { - application: string, - device: string, - ip: string, - location: string, - time?: string - }) => { + const Session = (auth: Authorization.authorization) => { const row = new Row({ - title: options.application, - subtitle: options.ip + ' - ' + options.location, + title: [auth.app_name, auth.app_version].join(' '), + subtitle: [auth.ip, auth.country].join(' - '), clickable: true, - titleRight: options.time + titleRight: auth.pFlags.current ? undefined : formatDateAccordingToToday(new Date(Math.max(auth.date_active, auth.date_created) * 1000)) }); + row.container.dataset.hash = auth.hash; + const midtitle = document.createElement('div'); midtitle.classList.add('row-midtitle'); - midtitle.innerHTML = options.device; + midtitle.innerHTML = [auth.device_model, auth.system_version].join(', '); row.subtitle.parentElement.insertBefore(midtitle, row.subtitle); return row; }; + const authorizations = this.authorizations.slice(); + { const section = new SettingSection({ name: 'Current Session' }); - const btnTerminate = Button('btn-primary btn-transparent danger', {icon: 'stop', text: 'Terminate all other sessions'}); + const auth = authorizations.findAndSplice(auth => auth.pFlags.current); + const session = Session(auth); - const session = Session({ - application: 'Telegram Web 1.0', - device: 'Safari, macOS', - ip: '216.3.128.12', - location: 'Paris, France' - }); + section.content.append(session.container); + + if(authorizations.length) { + const btnTerminate = Button('btn-primary btn-transparent danger', {icon: 'stop', text: 'Terminate all other sessions'}); + attachClickEvent(btnTerminate, (e) => { + new PopupConfirmAction('revoke-session', [{ + text: 'TERMINATE', + isDanger: true, + callback: () => { + toggleDisability([btnTerminate], true); + apiManager.invokeApi('auth.resetAuthorizations').then(value => { + //toggleDisability([btnTerminate], false); + btnTerminate.remove(); + otherSection.container.remove(); + }); + } + }], { + title: 'Terminate All Other Sessions', + text: 'Are you sure you want to terminate all other sessions?' + }).show(); + }); + + section.content.append(btnTerminate); + } - section.content.append(session.container, btnTerminate); this.scrollable.append(section.container); } - { - const section = new SettingSection({ - name: 'Other Sessions' - }); + if(!authorizations.length) { + return; + } - [Session({ - application: 'Telegram iOS 5.12', - device: 'iPhone X, iOS 13.2', - ip: '216.3.128.12', - location: 'Paris, France', - time: '19:25' - }), Session({ - application: 'Telegram Android 5.11', - device: 'Samsung Galaxy S9, Android 9P', - ip: '216.3.128.12', - location: 'Paris, France', - time: '16:34' - })].forEach(session => { - section.content.append(session.container); - }); + const otherSection = new SettingSection({ + name: 'Other Sessions' + }); - this.scrollable.append(section.container); + authorizations.forEach(auth => { + otherSection.content.append(Session(auth).container); + }); + + this.scrollable.append(otherSection.container); + + let target: HTMLElement; + const onTerminateClick = () => { + const hash = target.dataset.hash; + + new PopupConfirmAction('revoke-session', [{ + text: 'TERMINATE', + isDanger: true, + callback: () => { + apiManager.invokeApi('account.resetAuthorization', {hash}) + .then(value => { + if(value) { + target.remove(); + } + }, (err) => { + if(err.type === 'FRESH_RESET_AUTHORISATION_FORBIDDEN') { + toast('For security reasons, you can\'t terminate older sessions from a device that you\'ve just connected. Please use an earlier connection or wait for a few hours.'); + } + }); + } + }], { + title: 'Terminate Session', + text: 'Do you want to terminate this session?' + }).show(); + }; + + const element = this.menuElement = ButtonMenu([{ + icon: 'stop', + text: 'Terminate', + onClick: onTerminateClick + }]); + element.id = 'active-sessions-contextmenu'; + element.classList.add('contextmenu'); + + document.getElementById('page-chats').append(element); + + attachContextMenuListener(this.scrollable.container, (e) => { + target = findUpClassName(e.target, 'row'); + if(!target || target.dataset.hash === '0') { + return; + } + + if(e instanceof MouseEvent) e.preventDefault(); + // smth + if(e instanceof MouseEvent) e.cancelBubble = true; + + positionMenu(e, element); + openBtnMenu(element); + }); + + attachClickEvent(this.scrollable.container, (e) => { + target = findUpClassName(e.target, 'row'); + if(!target || target.dataset.hash === '0') { + return; + } + + onTerminateClick(); + }); + } + + onCloseAfterTimeout() { + if(this.menuElement) { + this.menuElement.remove(); } + + return super.onCloseAfterTimeout(); } } diff --git a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts index 83154a7a..688b8d47 100644 --- a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts +++ b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts @@ -1,7 +1,7 @@ import { SliderSuperTab } from "../../slider"; import { generateSection, SettingSection } from ".."; import Row from "../../row"; -import { AccountPassword, InputPrivacyKey, PrivacyRule } from "../../../layer"; +import { AccountPassword, Authorization, InputPrivacyKey, PrivacyRule } from "../../../layer"; import appPrivacyManager, { PrivacyType } from "../../../lib/appManagers/appPrivacyManager"; import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber"; import AppTwoStepVerificationTab from "./2fa"; @@ -14,6 +14,7 @@ import AppPrivacyForwardMessagesTab from "./privacy/forwardMessages"; import AppPrivacyAddToGroupsTab from "./privacy/addToGroups"; import AppPrivacyCallsTab from "./privacy/calls"; import AppActiveSessionsTab from "./activeSessions"; +import apiManager from "../../../lib/mtproto/mtprotoworker"; export default class AppPrivacyAndSecurityTab extends SliderSuperTab { protected init() { @@ -62,11 +63,14 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab { const activeSessionRow = new Row({ icon: 'activesessions', title: 'Active Sessions', - subtitle: '3 devices', + subtitle: 'Loading...', clickable: () => { - new AppActiveSessionsTab(this.slider).open(); + const tab = new AppActiveSessionsTab(this.slider); + tab.authorizations = authorizations; + tab.open(); } }); + activeSessionRow.freezed = true; section.content.append(blockedUsersRow.container, twoFactorRow.container, activeSessionRow.container); this.scrollable.append(section.container); @@ -76,7 +80,15 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab { twoFactorRow.subtitle.innerText = state.pFlags.has_password ? 'On' : 'Off'; twoFactorRow.freezed = false; - console.log('password state', state); + //console.log('password state', state); + }); + + let authorizations: Authorization.authorization[]; + apiManager.invokeApi('account.getAuthorizations').then(auths => { + activeSessionRow.freezed = false; + authorizations = auths.authorizations; + activeSessionRow.subtitle.innerText = authorizations.length + ' ' + (authorizations.length > 1 ? 'devices' : 'device'); + console.log('auths', auths); }); } @@ -149,7 +161,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab { }); } - container.append(numberVisibilityRow.container, lastSeenTimeRow.container, photoVisibilityRow.container,/* callRow.container, */linkAccountRow.container, groupChatsAddRow.container); + container.append(numberVisibilityRow.container, lastSeenTimeRow.container, photoVisibilityRow.container, callRow.container, linkAccountRow.container, groupChatsAddRow.container); } } } diff --git a/src/index.hbs b/src/index.hbs index 98a84300..0449946d 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -206,13 +206,6 @@ -
- - - - - -