diff --git a/app/js/controllers.js b/app/js/controllers.js
index b30bb006..ef550a60 100644
--- a/app/js/controllers.js
+++ b/app/js/controllers.js
@@ -2647,6 +2647,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
modal.result['finally'](updatePasswordState);
};
+ $scope.showSessions = function () {
+ $modal.open({
+ templateUrl: templateUrl('sessions_list_modal'),
+ controller: 'SessionsListModalController',
+ windowClass: 'md_simple_modal_window mobile_modal'
+ });
+ };
+
function updatePasswordState () {
$timeout.cancel(updatePasswordTimeout);
updatePasswordTimeout = false;
@@ -2984,6 +2992,58 @@ angular.module('myApp.controllers', ['myApp.i18n'])
})
})
+ .controller('SessionsListModalController', function ($scope, $q, $timeout, _, MtpApiManager, ErrorService, $modalInstance) {
+
+ $scope.slice = {limit: 20, limitDelta: 20};
+
+ var updateSessionsTimeout = false;
+
+ function updateSessions () {
+ $timeout.cancel(updateSessionsTimeout);
+ MtpApiManager.invokeApi('account.getAuthorizations').then(function (result) {
+ $scope.sessionsLoaded = true;
+ $scope.authorizations = result.authorizations;
+
+ var authorization;
+ for (var i = 0, len = $scope.authorizations.length; i < len; i++) {
+ authorization = $scope.authorizations[i];
+ authorization.current = (authorization.flags & 1) == 1;
+ }
+ $scope.authorizations.sort(function (sA, sB) {
+ if (sA.current) {
+ return -1;
+ }
+ if (sB.current) {
+ return 1;
+ }
+ return sB.date_active - sA.date_active;
+ });
+ updateSessionsTimeout = $timeout(updateSessions, 5000);
+ })
+ }
+
+ $scope.terminateSession = function (hash) {
+ ErrorService.confirm({type: 'TERMINATE_SESSION'}).then(function () {
+ MtpApiManager.invokeApi('account.resetAuthorization', {hash: hash}).then(updateSessions);
+ })
+ };
+
+ $scope.terminateAllSessions = function () {
+ ErrorService.confirm({type: 'TERMINATE_SESSIONS'}).then(function () {
+ MtpApiManager.invokeApi('auth.resetAuthorizations', {});
+ });
+ };
+
+ updateSessions();
+
+ $scope.$on('apiUpdate', function (e, update) {
+ if (update._ == 'updateNewAuthorization') {
+ updateSessions();
+ }
+ });
+
+ })
+
.controller('PasswordUpdateModalController', function ($scope, $q, _, PasswordManager, MtpApiManager, ErrorService, $modalInstance) {
$scope.passwordSettings = {};
diff --git a/app/js/directives.js b/app/js/directives.js
index 914eff4a..3f5dc052 100755
--- a/app/js/directives.js
+++ b/app/js/directives.js
@@ -843,6 +843,33 @@ angular.module('myApp.directives', ['myApp.filters'])
})
+ .directive('mySessionsList', function($window, $timeout) {
+
+ return {
+ link: link
+ };
+
+ function link ($scope, element, attrs) {
+ var sessionsWrap = $('.sessions_wrap', element)[0];
+
+ onContentLoaded(function () {
+ $(sessionsWrap).nanoScroller({preventPageScrolling: true, tabIndex: -1, iOSNativeScrolling: true});
+ updateSizes();
+ });
+
+ function updateSizes () {
+ $(element).css({
+ height: $($window).height()
+ - (Config.Mobile ? 46 + 18 : 200)
+ });
+ $(sessionsWrap).nanoScroller();
+ }
+
+ $($window).on('resize', updateSizes);
+ };
+
+ })
+
.directive('myHistory', function ($window, $timeout, $rootScope, $transition) {
return {
diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json
index 01363a9d..9dd4a4e0 100644
--- a/app/js/locales/en-us.json
+++ b/app/js/locales/en-us.json
@@ -37,6 +37,7 @@
"settings_modal_edit_username": "Change username",
"settings_modal_empty_username_set": "Set username",
"settings_modal_terminate_sessions": "Terminate all sessions",
+ "settings_modal_active_sessions": "Active sessions",
"settings_modal_settings": "Settings",
"settings_modal_notification_alert": "Notification alerts",
"settings_modal_vibrate": "Vibrate",
@@ -74,6 +75,14 @@
"password_delete_active": "Deleting...",
"password_delete_submit": "Delete password",
+ "sessions_modal_title": "Active Sessions",
+ "sessions_modal_loading": "Loading{dots}",
+ "sessions_modal_current_session": "Current session",
+ "sessions_modal_session_online": "online",
+ "sessions_modal_terminate_one": "Terminate",
+ "sessions_modal_terminate_all": "Terminate all other sessions",
+ "sessions_modal_active_sessions": "Active sessions",
+
"page_title_pluralize_notifications": "{'0': 'No notifications', 'one': '1 notification', 'other': '{} notifications'}",
"profile_edit_modal_title": "Edit profile",
@@ -152,7 +161,8 @@
"confirm_modal_logout": "Are you sure you want to log out?",
"confirm_modal_update_reload": "A new version of Telegram Web has been downloaded. Launch it?",
"confirm_modal_history_flush": "Are you sure? This can not be undone!",
- "confirm_modal_terminate_sessions": "Are you sure you want to log out all devices except for the current one?",
+ "confirm_modal_terminate_sessions": "Are you sure you want to log out all devices except for this one?",
+ "confirm_modal_terminate_session": "Are you sure you want to log out this device?",
"confirm_modal_clipboard_file_send": "Are you sure to send file(s) from clipboard?",
"confirm_modal_clipboard_X_files_send": "{'one': 'Are you sure to send file from clipboard?', 'other': 'Are you sure to send {} files from clipboard?'}",
"confirm_modal_message_delete": "Are you sure you want to delete the message?",
diff --git a/app/less/app.less b/app/less/app.less
index c2d7e217..6215999d 100644
--- a/app/less/app.less
+++ b/app/less/app.less
@@ -2778,6 +2778,61 @@ a.contacts_modal_contact:hover .md_modal_list_peer_description,
}
}
+
+.sessions_modal {
+ &_session {
+ padding: 8px 16px;
+ }
+ &_sessions_header {
+ color: #999;
+ font-size: 13px;
+ margin-left: 16px;
+ margin-top: 20px;
+ font-weight: bold;
+ }
+ &_terminate_all_wrap {
+ margin: 5px 0 5px;
+ text-align: center;
+ }
+}
+.sessions_wrap {
+
+}
+.sessions_scrollable_wrap {
+
+}
+.session_active_date_online {
+ color: #3a6d99;
+}
+.sessions_modal_loading {
+ text-align: center;
+ color: #999;
+ font-size: 16px;
+ line-height: 18px;
+ padding: 1px 50px;
+ margin: 0;
+}
+.session_active_date {
+ color: #999;
+}
+.session_meta_wrap {
+ text-align: right;
+}
+.session_terminate_btn {
+ margin-top: 17px;
+}
+.sessions_modal_session_app {
+ font-weight: bold;
+ font-style: 12px;
+ margin-bottom: 3px;
+}
+.sessions_modal_session_device {
+ margin: 3px 0 3px;
+}
+.sessions_modal_session_location {
+ color: #777;
+}
+
.modal-dialog {
.md_simple_modal_window &,
.confirm_modal_window &,
diff --git a/app/less/desktop.less b/app/less/desktop.less
index 2613604a..e1c31771 100644
--- a/app/less/desktop.less
+++ b/app/less/desktop.less
@@ -645,6 +645,7 @@ a.footer_link.active:active {
.nano-pane {
.im_history_col .nano > &,
.contacts_modal_col .nano > &,
+ .sessions_modal_col .nano > &,
.im_dialogs_modal_col .nano > & {
background : rgba(216,223,225,0.45); /*45% d8dfe5*/
width : 9px;
@@ -660,6 +661,13 @@ a.footer_link.active:active {
right: 4px;
}
+ .sessions_modal_col .nano > & {
+ top: 4px;
+ bottom: 4px;
+ width: 5px;
+ right: 4px;
+ }
+
.im_dialogs_modal_col .nano > & {
width: 6px;
right: 2px;
@@ -672,6 +680,7 @@ a.footer_link.active:active {
.im_history_col .nano > &,
.contacts_modal_col .nano > &,
+ .sessions_modal_col .nano > &,
.im_dialogs_modal_col .nano > & {
& > .nano-slider {
background : rgba(137,160,179,0.50); /*50% 89a0b3*/
@@ -753,6 +762,13 @@ a.footer_link.active:active {
}
}
+.session_terminate_btn {
+ opacity: 0;
+}
+.sessions_modal_session:hover .session_terminate_btn {
+ opacity: 1;
+}
+
.icon-message-status {
pointer-events: none;
background: #4eabf1;
diff --git a/app/partials/desktop/confirm_modal.html b/app/partials/desktop/confirm_modal.html
index 6d9f866d..5e5dd2ed 100644
--- a/app/partials/desktop/confirm_modal.html
+++ b/app/partials/desktop/confirm_modal.html
@@ -7,6 +7,7 @@
+
+
+
+
+
+
+
diff --git a/app/partials/desktop/settings_modal.html b/app/partials/desktop/settings_modal.html
index 3504400f..9b3c49ca 100644
--- a/app/partials/desktop/settings_modal.html
+++ b/app/partials/desktop/settings_modal.html
@@ -125,7 +125,7 @@