Browse Source

Active sessions functions

master
Eduard Kuzmenko 4 years ago
parent
commit
ac313c94bd
  1. 1
      src/components/chat/contextMenu.ts
  2. 1
      src/components/dialogsContextMenu.ts
  3. 18
      src/components/popups/index.ts
  4. 161
      src/components/sidebarLeft/tabs/activeSessions.ts
  5. 22
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  6. 7
      src/index.hbs
  7. 3
      src/scss/partials/_button.scss
  8. 6
      src/scss/partials/_leftSidebar.scss
  9. 13
      src/scss/style.scss

1
src/components/chat/contextMenu.ts

@ -292,6 +292,7 @@ export default class ChatContextMenu {
this.element = ButtonMenu(this.buttons, this.chat.bubbles.listenerSetter); this.element = ButtonMenu(this.buttons, this.chat.bubbles.listenerSetter);
this.element.id = 'bubble-contextmenu'; this.element.id = 'bubble-contextmenu';
this.element.classList.add('contextmenu');
this.chat.container.append(this.element); this.chat.container.append(this.element);
}; };

1
src/components/dialogsContextMenu.ts

@ -83,6 +83,7 @@ export default class DialogsContextMenu {
this.element = ButtonMenu(this.buttons); this.element = ButtonMenu(this.buttons);
this.element.id = 'dialogs-contextmenu'; this.element.id = 'dialogs-contextmenu';
this.element.classList.add('contextmenu');
document.getElementById('page-chats').append(this.element); document.getElementById('page-chats').append(this.element);
} }

18
src/components/popups/index.ts

@ -50,17 +50,17 @@ export default class PopupElement {
this.header.prepend(this.btnClose); this.header.prepend(this.btnClose);
this.btnClose.addEventListener('click', this.hide, {once: true}); this.btnClose.addEventListener('click', this.hide, {once: true});
}
if(options.overlayClosable) { if(options.overlayClosable) {
const onOverlayClick = (e: MouseEvent) => { const onOverlayClick = (e: MouseEvent) => {
if(!findUpClassName(e.target, 'popup-container')) { if(!findUpClassName(e.target, 'popup-container')) {
this.btnClose.click(); this.hide();
this.element.removeEventListener('click', onOverlayClick); this.element.removeEventListener('click', onOverlayClick);
} }
}; };
this.element.addEventListener('click', onOverlayClick); this.element.addEventListener('click', onOverlayClick);
}
} }
if(options.withConfirm) { if(options.withConfirm) {

161
src/components/sidebarLeft/tabs/activeSessions.ts

@ -2,75 +2,158 @@ import { SliderSuperTab } from "../../slider";
import { SettingSection } from ".."; import { SettingSection } from "..";
import Button from "../../button"; import Button from "../../button";
import Row from "../../row"; 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 { export default class AppActiveSessionsTab extends SliderSuperTab {
public authorizations: Authorization.authorization[];
private menuElement: HTMLElement;
protected init() { protected init() {
this.container.classList.add('active-sessions-container'); this.container.classList.add('active-sessions-container');
this.title.innerText = 'Active Sessions'; this.title.innerText = 'Active Sessions';
const Session = (options: { const Session = (auth: Authorization.authorization) => {
application: string,
device: string,
ip: string,
location: string,
time?: string
}) => {
const row = new Row({ const row = new Row({
title: options.application, title: [auth.app_name, auth.app_version].join(' '),
subtitle: options.ip + ' - ' + options.location, subtitle: [auth.ip, auth.country].join(' - '),
clickable: true, 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'); const midtitle = document.createElement('div');
midtitle.classList.add('row-midtitle'); 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); row.subtitle.parentElement.insertBefore(midtitle, row.subtitle);
return row; return row;
}; };
const authorizations = this.authorizations.slice();
{ {
const section = new SettingSection({ const section = new SettingSection({
name: 'Current Session' 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({ section.content.append(session.container);
application: 'Telegram Web 1.0',
device: 'Safari, macOS', if(authorizations.length) {
ip: '216.3.128.12', const btnTerminate = Button('btn-primary btn-transparent danger', {icon: 'stop', text: 'Terminate all other sessions'});
location: 'Paris, France' 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); this.scrollable.append(section.container);
} }
{ if(!authorizations.length) {
const section = new SettingSection({ return;
name: 'Other Sessions' }
});
[Session({ const otherSection = new SettingSection({
application: 'Telegram iOS 5.12', name: 'Other Sessions'
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);
});
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();
} }
} }

22
src/components/sidebarLeft/tabs/privacyAndSecurity.ts

@ -1,7 +1,7 @@
import { SliderSuperTab } from "../../slider"; import { SliderSuperTab } from "../../slider";
import { generateSection, SettingSection } from ".."; import { generateSection, SettingSection } from "..";
import Row from "../../row"; 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 appPrivacyManager, { PrivacyType } from "../../../lib/appManagers/appPrivacyManager";
import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber"; import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber";
import AppTwoStepVerificationTab from "./2fa"; import AppTwoStepVerificationTab from "./2fa";
@ -14,6 +14,7 @@ import AppPrivacyForwardMessagesTab from "./privacy/forwardMessages";
import AppPrivacyAddToGroupsTab from "./privacy/addToGroups"; import AppPrivacyAddToGroupsTab from "./privacy/addToGroups";
import AppPrivacyCallsTab from "./privacy/calls"; import AppPrivacyCallsTab from "./privacy/calls";
import AppActiveSessionsTab from "./activeSessions"; import AppActiveSessionsTab from "./activeSessions";
import apiManager from "../../../lib/mtproto/mtprotoworker";
export default class AppPrivacyAndSecurityTab extends SliderSuperTab { export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
protected init() { protected init() {
@ -62,11 +63,14 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const activeSessionRow = new Row({ const activeSessionRow = new Row({
icon: 'activesessions', icon: 'activesessions',
title: 'Active Sessions', title: 'Active Sessions',
subtitle: '3 devices', subtitle: 'Loading...',
clickable: () => { 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); section.content.append(blockedUsersRow.container, twoFactorRow.container, activeSessionRow.container);
this.scrollable.append(section.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.subtitle.innerText = state.pFlags.has_password ? 'On' : 'Off';
twoFactorRow.freezed = false; 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);
} }
} }
} }

7
src/index.hbs

@ -206,13 +206,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="btn-menu" id="dialogs-contextmenu">
<div class="btn-menu-item menu-unread tgico rp"><div></div></div>
<div class="btn-menu-item menu-pin tgico rp"><div></div></div>
<div class="btn-menu-item menu-mute tgico rp"><div></div></div>
<div class="btn-menu-item menu-archive tgico rp"><div></div></div>
<div class="btn-menu-item menu-delete tgico-delete danger rp"><div></div></div>
</div>
<div class="emoji-dropdown" id="emoji-dropdown" style="display: none;"> <div class="emoji-dropdown" id="emoji-dropdown" style="display: none;">
<div class="emoji-container"> <div class="emoji-container">
<div class="tabs-container"> <div class="tabs-container">

3
src/scss/partials/_button.scss

@ -145,9 +145,6 @@
height: 56px; height: 56px;
cursor: pointer !important; cursor: pointer !important;
pointer-events: all !important; pointer-events: all !important;
background-position: 16px center;
background-size: 24px 24px;
background-repeat: no-repeat;
color: #000; color: #000;
text-transform: none; text-transform: none;
white-space: nowrap; white-space: nowrap;

6
src/scss/partials/_leftSidebar.scss

@ -1017,6 +1017,12 @@
color: var(--color-text-secondary); color: var(--color-text-secondary);
line-height: 1.5; line-height: 1.5;
} }
&-midtitle, &-subtitle {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
} }
.sidebar-left-section:first-child { .sidebar-left-section:first-child {

13
src/scss/style.scss

@ -382,12 +382,12 @@ input, textarea {
background: #fed85a; background: #fed85a;
} }
#bubble-contextmenu, #dialogs-contextmenu { .contextmenu {
position: fixed; position: fixed !important;
right: auto; right: auto !important;
bottom: auto; bottom: auto !important;
width: auto; width: auto !important;
z-index: 4; z-index: 4 !important;
} }
@keyframes fade-in-opacity { @keyframes fade-in-opacity {
@ -458,6 +458,7 @@ input, textarea {
border-radius: $border-radius-medium; border-radius: $border-radius-medium;
animation: fade-in-opacity-fade-out-opacity 3s linear forwards; animation: fade-in-opacity-fade-out-opacity 3s linear forwards;
z-index: 5; z-index: 5;
max-width: 22.5rem;
} }
hr { hr {

Loading…
Cancel
Save