Basic Safari, Mozilla support, perfomance improvements, bugfixes
* Added data:image/jpeg;base64, mode for small images on browsers, which dont support LocalFileSystem. Please use Chrome for full experience * Speed-up chat modal window, by making getFullChat request asynchronous * Added basic error handling for photo/video modal windows * Improved dateOrTime filter perfomance by caching its values * Added sentMessages cleanup * Guest DCs networkers now dont perform http_wait if no sentMessages pending * Added favicon blinking on new message
This commit is contained in:
parent
deae4717a6
commit
ca781f6c86
@ -226,6 +226,7 @@ fieldset[disabled] .btn-tg.active {
|
||||
}
|
||||
|
||||
|
||||
|
||||
.img_fullsize_with_progress_wrap {
|
||||
position: relative;
|
||||
/*margin: 0 auto 20px;*/
|
||||
@ -286,7 +287,8 @@ fieldset[disabled] .btn-tg.active {
|
||||
|
||||
.im_page_wrap {
|
||||
background: #FFF;
|
||||
max-width: 1000px;
|
||||
/*max-width: 1000px;*/
|
||||
width: 1000px;
|
||||
margin: 0 auto;
|
||||
|
||||
-webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1);
|
||||
@ -328,11 +330,13 @@ fieldset[disabled] .btn-tg.active {
|
||||
|
||||
|
||||
/* Dialogs list */
|
||||
|
||||
.im_dialogs_col {
|
||||
width: 315px;
|
||||
}
|
||||
.im_dialogs_col .nano > .pane {
|
||||
background : rgba(0,0,0,.0);
|
||||
width : 12px;
|
||||
right: -7px;
|
||||
right: 0px;
|
||||
-webkit-transition : .2s;
|
||||
-moz-transition : .2s;
|
||||
-o-transition : .2s;
|
||||
@ -372,15 +376,10 @@ fieldset[disabled] .btn-tg.active {
|
||||
|
||||
|
||||
.im_dialogs_wrap {
|
||||
overflow: visible;
|
||||
overflow-x: visible !important;
|
||||
overflow-y: visible;
|
||||
}
|
||||
.im_dialogs_scrollable_wrap {
|
||||
/*border-right: 2px solid #E9EBED;*/
|
||||
padding: 0 12px;
|
||||
padding: 0 19px 0 12px;
|
||||
outline: none ! important;
|
||||
overflow-x: visible;
|
||||
}
|
||||
.im_dialogs_scrollable_wrap .nav-stacked > li + li {
|
||||
margin-top: 0;
|
||||
@ -538,6 +537,9 @@ a.im_dialog:hover .im_dialog_message_text {
|
||||
-moz-box-shadow: 0px 2px 0px rgba(0, 0, 0, 0.12);
|
||||
box-shadow: 0px 2px 0px rgba(0, 0, 0, 0.12);
|
||||
margin-right: 15px;
|
||||
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.im_history_panel {
|
||||
padding: 10px 4px 0;
|
||||
@ -1104,7 +1106,6 @@ img.img_fullsize {
|
||||
.chat_modal_window .modal-dialog {
|
||||
max-width: 506px;
|
||||
}
|
||||
|
||||
.chat_modal_wrap .modal-body {
|
||||
padding: 23px 25px 15px;
|
||||
}
|
||||
@ -1253,4 +1254,32 @@ img.img_fullsize {
|
||||
.error_modal_description {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.photo_modal_error {
|
||||
color: #999;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 50%;
|
||||
margin-top: -20px;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
font-size: 1.4em;
|
||||
line-height: 160%;
|
||||
}
|
||||
|
||||
.video_full_error {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: rgba(0,0,0,0.6);
|
||||
color: #FFF;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin: -40px 10px 0;
|
||||
padding: 10px 10px;
|
||||
text-align: center;
|
||||
font-size: 1.4em;
|
||||
line-height: 160%;
|
||||
}
|
BIN
app/favicon_unread.ico
Normal file
BIN
app/favicon_unread.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 738 B |
BIN
app/img/placeholders/PhotoThumbConversation.gif
Normal file
BIN
app/img/placeholders/PhotoThumbConversation.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 B |
BIN
app/img/placeholders/PhotoThumbModal.gif
Normal file
BIN
app/img/placeholders/PhotoThumbModal.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 B |
BIN
app/img/placeholders/VideoThumbConversation.gif
Normal file
BIN
app/img/placeholders/VideoThumbConversation.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 B |
BIN
app/img/placeholders/VideoThumbModal.gif
Normal file
BIN
app/img/placeholders/VideoThumbModal.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 B |
@ -7,7 +7,7 @@
|
||||
<link rel="stylesheet" href="vendor/angular/angular-csp.css"/>
|
||||
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.css"/>
|
||||
<link rel="stylesheet" href="vendor/jquery.nanoscroller/nanoscroller.css"/>
|
||||
<link rel="stylesheet" href="css/app.css?3"/>
|
||||
<link rel="stylesheet" href="css/app.css?4"/>
|
||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
@ -33,14 +33,14 @@
|
||||
|
||||
|
||||
<script type="text/javascript" src="js/lib/config.js"></script>
|
||||
<script type="text/javascript" src="js/lib/mtproto.js?1"></script>
|
||||
<script type="text/javascript" src="js/lib/mtproto.js?2"></script>
|
||||
|
||||
<script type="text/javascript" src="js/util.js"></script>
|
||||
<script type="text/javascript" src="js/app.js?2"></script>
|
||||
<script type="text/javascript" src="js/services.js?5"></script>
|
||||
<script type="text/javascript" src="js/controllers.js?4"></script>
|
||||
<script type="text/javascript" src="js/filters.js?2"></script>
|
||||
<script type="text/javascript" src="js/directives.js?4"></script>
|
||||
<script type="text/javascript" src="js/app.js?3"></script>
|
||||
<script type="text/javascript" src="js/services.js?6"></script>
|
||||
<script type="text/javascript" src="js/controllers.js?5"></script>
|
||||
<script type="text/javascript" src="js/filters.js?3"></script>
|
||||
<script type="text/javascript" src="js/directives.js?5"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -25,7 +25,7 @@ angular.module('myApp', [
|
||||
config(['$locationProvider', '$routeProvider', '$compileProvider', function($locationProvider, $routeProvider, $compileProvider) {
|
||||
|
||||
var icons = {}, reverseIcons = {}, i, j, hex, name, dataItem,
|
||||
ranges = [[0x1f600, 0x1f637], [0x270a, 0x270c], [0x1f446, 0x1f450]];
|
||||
ranges = [[0x1f600, 0x1f637], [0x261d, 0x263f], [0x270a, 0x270c], [0x1f446, 0x1f450]];
|
||||
|
||||
for (j in ranges) {
|
||||
for (i = ranges[j][0]; i <= ranges[j][1]; i++) {
|
||||
|
@ -121,7 +121,8 @@ angular.module('myApp.controllers', [])
|
||||
$scope.isLoggedIn = true;
|
||||
$scope.logOut = function () {
|
||||
MtpApiManager.logOut().then(function () {
|
||||
location.href = 'login';
|
||||
location.hash = '/login';
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
@ -487,8 +488,17 @@ angular.module('myApp.controllers', [])
|
||||
};
|
||||
})
|
||||
|
||||
.controller('ChatModalController', function ($scope, AppUsersManager, AppChatsManager, fullChat) {
|
||||
$scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, fullChat);
|
||||
.controller('ChatModalController', function ($scope, $timeout, AppUsersManager, AppChatsManager, MtpApiManager) {
|
||||
$scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, {});
|
||||
|
||||
MtpApiManager.invokeApi('messages.getFullChat', {
|
||||
chat_id: $scope.chatID
|
||||
}).then(function (result) {
|
||||
AppChatsManager.saveApiChats(result.chats);
|
||||
AppUsersManager.saveApiUsers(result.users);
|
||||
|
||||
$scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, result.full_chat);
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
restrict: 'AE',
|
||||
scope: true,
|
||||
translude: false,
|
||||
templateUrl: 'partials/dialog.html?1'
|
||||
templateUrl: 'partials/dialog.html?2'
|
||||
};
|
||||
})
|
||||
|
||||
@ -25,7 +25,7 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
restrict: 'AE',
|
||||
scope: true,
|
||||
translude: false,
|
||||
templateUrl: 'partials/message.html?2'
|
||||
templateUrl: 'partials/message.html?3'
|
||||
};
|
||||
})
|
||||
|
||||
@ -399,6 +399,10 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
<img class="photo_modal_image" width="{{fullPhoto.width}}" height="{{fullPhoto.height}}" />\
|
||||
</a>\
|
||||
</div>\
|
||||
<div class="photo_modal_error_wrap" ng-if="error">\
|
||||
<div class="photo_modal_error" ng-if="error.html" ng-bind-html="error.html"></div>\
|
||||
<div class="photo_modal_error" ng-if="error.text">{{error.text}}</div>\
|
||||
</div>\
|
||||
</div>',
|
||||
scope: {
|
||||
fullPhoto: '=',
|
||||
@ -411,8 +415,9 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
fullLoaded = false;
|
||||
|
||||
|
||||
imgElement.attr('src', scope.fullPhoto.placeholder || 'img/blank.gif');
|
||||
|
||||
if (!scope.fullPhoto.location) {
|
||||
imgElement.attr('src', scope.fullPhoto.placeholder || '');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -450,10 +455,12 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
}, function (e) {
|
||||
dLog('Download image failed', e, scope.fullPhoto.location);
|
||||
scope.progress.enabled = false;
|
||||
imgElement
|
||||
.attr('src', scope.fullPhoto.placeholder || '')
|
||||
.removeClass('thumb_blurred');
|
||||
|
||||
if (e && e.type == 'FS_BROWSER_UNSUPPORTED') {
|
||||
scope.error = {html: 'Your browser doesn\'t support <a href="https://developer.mozilla.org/en-US/docs/Web/API/LocalFileSystem" target="_blank">LocalFileSystem</a> feature which is needed to display this image.<br/>Please, install <a href="http://google.com/chrome" target="_blank">Google Chrome</a> or use <a href="https://telegram.org/" target="_blank">mobile app</a> instead.'};
|
||||
} else {
|
||||
scope.error = {text: 'Download failed', error: e};
|
||||
}
|
||||
}, function (progress) {
|
||||
scope.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total));
|
||||
});
|
||||
@ -487,6 +494,10 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
<source ng-src="{{player.src}}" type="video/mp4">\
|
||||
</video>\
|
||||
</div>\
|
||||
<div class="video_full_error_wrap" ng-if="error">\
|
||||
<div class="video_full_error" ng-if="error.html" ng-bind-html="error.html"></div>\
|
||||
<div class="video_full_error" ng-if="error.text">{{error.text}}</div>\
|
||||
</div>\
|
||||
</div>',
|
||||
scope: {
|
||||
video: '='
|
||||
@ -519,9 +530,16 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
scope.player.quicktime = hasQt;
|
||||
scope.player.src = $sce.trustAsResourceUrl(url);
|
||||
}, function (e) {
|
||||
dLog('Download image failed', e, scope.fullPhoto.location);
|
||||
dLog('Download video failed', e, scope.video);
|
||||
scope.progress.enabled = false;
|
||||
scope.player.src = '';
|
||||
|
||||
if (e && e.type == 'FS_BROWSER_UNSUPPORTED') {
|
||||
scope.error = {html: 'Your browser doesn\'t support <a href="https://developer.mozilla.org/en-US/docs/Web/API/LocalFileSystem" target="_blank">LocalFileSystem</a> feature which is needed to play this video.<br/>Please, install <a href="http://google.com/chrome" target="_blank">Google Chrome</a> or use <a href="https://telegram.org/" target="_blank">mobile app</a> instead.'};
|
||||
} else {
|
||||
scope.error = {text: 'Video download failed', error: e};
|
||||
}
|
||||
|
||||
}, function (progress) {
|
||||
scope.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total));
|
||||
});
|
||||
|
@ -52,7 +52,14 @@ angular.module('myApp.filters', [])
|
||||
}])
|
||||
|
||||
.filter('dateOrTime', ['$filter', function($filter) {
|
||||
var cachedDates = {};
|
||||
|
||||
return function (timestamp) {
|
||||
|
||||
if (cachedDates[timestamp]) {
|
||||
return cachedDates[timestamp];
|
||||
}
|
||||
|
||||
var ticks = timestamp * 1000,
|
||||
diff = Math.abs(+new Date() - ticks),
|
||||
format = 'HH:mm';
|
||||
@ -63,7 +70,7 @@ angular.module('myApp.filters', [])
|
||||
else if (diff > 43200000) { // 12 hours
|
||||
format = 'EEE';
|
||||
}
|
||||
return $filter('date')(ticks, format);
|
||||
return cachedDates[timestamp] = $filter('date')(ticks, format);
|
||||
}
|
||||
}])
|
||||
|
||||
|
@ -61,6 +61,40 @@ function bytesFromHex (hexString) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function bytesToBase64 (bytes) {
|
||||
var mod3, result = '';
|
||||
|
||||
for (var nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
|
||||
mod3 = nIdx % 3;
|
||||
nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24);
|
||||
if (mod3 === 2 || nLen - nIdx === 1) {
|
||||
result += String.fromCharCode(
|
||||
uint6ToBase64(nUint24 >>> 18 & 63),
|
||||
uint6ToBase64(nUint24 >>> 12 & 63),
|
||||
uint6ToBase64(nUint24 >>> 6 & 63),
|
||||
uint6ToBase64(nUint24 & 63)
|
||||
);
|
||||
nUint24 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result.replace(/A(?=A$|$)/g, '=');
|
||||
}
|
||||
|
||||
function uint6ToBase64 (nUint6) {
|
||||
return nUint6 < 26
|
||||
? nUint6 + 65
|
||||
: nUint6 < 52
|
||||
? nUint6 + 71
|
||||
: nUint6 < 62
|
||||
? nUint6 - 4
|
||||
: nUint6 === 62
|
||||
? 43
|
||||
: nUint6 === 63
|
||||
? 47
|
||||
: 65;
|
||||
}
|
||||
|
||||
function bytesCmp (bytes1, bytes2) {
|
||||
var len = bytes1.length;
|
||||
if (len != bytes2.length) {
|
||||
@ -120,14 +154,7 @@ function bytesFromBigInt (bigInt, len) {
|
||||
}
|
||||
|
||||
function bytesToArrayBuffer (b) {
|
||||
var len = b.length,
|
||||
array = new Uint8Array(len);
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
array[i] = b[i];
|
||||
}
|
||||
|
||||
return array.buffer;
|
||||
return (new Uint8Array(b)).buffer;
|
||||
}
|
||||
|
||||
function bytesFromArrayBuffer (buffer) {
|
||||
@ -1482,14 +1509,21 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
|
||||
this.serverSalt = serverSalt;
|
||||
|
||||
// if (1 == dcID) {
|
||||
// var self = this;
|
||||
// (function () {
|
||||
// dLog('update server salt');
|
||||
// self.serverSalt = [0,0,0,0,0,0,0,0];
|
||||
// setTimeout(arguments.callee, nextRandomInt(2000, 12345));
|
||||
// })();
|
||||
// }
|
||||
|
||||
this.sessionID = new Array(8);
|
||||
MtpSecureRandom.nextBytes(this.sessionID);
|
||||
|
||||
if (true) {
|
||||
this.sessionID[0] = 0xA;
|
||||
this.sessionID[1] = 0xB;
|
||||
this.sessionID[3] = 0xC;
|
||||
this.sessionID[4] = 0xD;
|
||||
if (false) {
|
||||
this.sessionID[0] = 0xAB;
|
||||
this.sessionID[1] = 0xCD;
|
||||
}
|
||||
|
||||
this.seqNo = 0;
|
||||
@ -1594,7 +1628,14 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
if (this.longPollPending && (new Date().getTime()) < this.longPollPending) {
|
||||
return false;
|
||||
}
|
||||
this.sendLongPoll();
|
||||
var self = this;
|
||||
AppConfigManager.get('dc').then(function (baseDcID) {
|
||||
if (baseDcID != self.dcID && self.cleanupSent()) {
|
||||
// console.warn('send long-poll for guest DC is delayed', self.dcID);
|
||||
return;
|
||||
}
|
||||
self.sendLongPoll();
|
||||
});
|
||||
};
|
||||
|
||||
MtpNetworker.prototype.sendLongPoll = function() {
|
||||
@ -1741,22 +1782,24 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
}
|
||||
|
||||
this.pendingAcks = [];
|
||||
|
||||
var self = this;
|
||||
|
||||
this.sendEncryptedRequest(message).then(function (result) {
|
||||
self.parseResponse(result.data);
|
||||
self.parseResponse(result.data).then(function (response) {
|
||||
// dLog('Server response', self.dcID, response);
|
||||
|
||||
if (noResponseMsgs.length) {
|
||||
$timeout(function () {
|
||||
angular.forEach(noResponseMsgs, function (msgID) {
|
||||
if (self.sentMessages[msgID]) {
|
||||
var deferred = self.sentMessages[msgID].deferred;
|
||||
delete self.sentMessages[msgID];
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
self.processMessage(response.response, response.messageID, response.sessionID);
|
||||
|
||||
angular.forEach(noResponseMsgs, function (msgID) {
|
||||
if (self.sentMessages[msgID]) {
|
||||
var deferred = self.sentMessages[msgID].deferred;
|
||||
delete self.sentMessages[msgID];
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
self.cleanupSent();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -1833,7 +1876,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
var dataLength = responseBuffer.byteLength - deserializer.getOffset();
|
||||
var encryptedData = deserializer.fetchRawBytes(dataLength, 'encrypted_data');
|
||||
|
||||
this.getDecryptedMessage(msgKey, encryptedData).then(function (dataWithPadding) {
|
||||
return this.getDecryptedMessage(msgKey, encryptedData).then(function (dataWithPadding) {
|
||||
var buffer = bytesToArrayBuffer(dataWithPadding);
|
||||
|
||||
var deserializer = new TLDeserialization(buffer, {mtproto: true});
|
||||
@ -1848,7 +1891,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
|
||||
var offset = deserializer.getOffset();
|
||||
|
||||
MtpSha1Service.hash(dataWithPadding.slice(0, offset)).then(function (dataHashed) {
|
||||
return MtpSha1Service.hash(dataWithPadding.slice(0, offset)).then(function (dataHashed) {
|
||||
if (!bytesCmp(msgKey, dataHashed.slice(-16))) {
|
||||
throw new Error('server msgKey mismatch');
|
||||
}
|
||||
@ -1858,9 +1901,12 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
|
||||
var response = deserializer.fetchObject('', 'INPUT');
|
||||
|
||||
// dLog('Server response', response);
|
||||
|
||||
self.processResponse(response, messageID, sessionID, seqNo);
|
||||
return {
|
||||
response: response,
|
||||
messageID: messageID,
|
||||
sessionID: sessionID,
|
||||
seqNo: seqNo
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -1908,10 +1954,35 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
this.sheduleRequest(100);
|
||||
};
|
||||
|
||||
MtpNetworker.prototype.processResponse = function (response, messageID, sessionID, seqNo) {
|
||||
return this.processMessage(response, messageID, sessionID);
|
||||
MtpNetworker.prototype.cleanupSent = function () {
|
||||
var self = this;
|
||||
var notEmpty = false;
|
||||
// dLog('clean start', this.dcID/*, this.sentMessages*/);
|
||||
angular.forEach(this.sentMessages, function(message, msgID) {
|
||||
// dLog('clean iter', msgID, message);
|
||||
if (message.notContentRelated && self.pendingMessages[msgID] === undefined) {
|
||||
// dLog('clean notContentRelated', msgID);
|
||||
delete self.sentMessages[msgID];
|
||||
}
|
||||
else if (message.container) {
|
||||
for (var i = 0; i < message.inner.length; i++) {
|
||||
if (self.sentMessages[message.inner[i]] !== undefined) {
|
||||
// dLog('clean failed, found', msgID, message.inner[i], self.sentMessages[message.inner[i]].seq_no);
|
||||
notEmpty = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// dLog('clean container', msgID);
|
||||
delete self.sentMessages[msgID];
|
||||
} else {
|
||||
notEmpty = true;
|
||||
}
|
||||
});
|
||||
|
||||
return !notEmpty;
|
||||
};
|
||||
|
||||
|
||||
MtpNetworker.prototype.processMessageAck = function (messageID) {
|
||||
var sentMessage = this.sentMessages[messageID];
|
||||
if (sentMessage && !sentMessage.acked) {
|
||||
@ -1948,6 +2019,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
break;
|
||||
|
||||
case 'bad_server_salt':
|
||||
dLog('bad server salt', message);
|
||||
var sentMsg = this.sentMessages[message.bad_msg_id];
|
||||
if (!sentMsg || sentMsg.seq_no != message.bad_msg_seqno) {
|
||||
dLog(message.bad_msg_id, message.bad_msg_seqno);
|
||||
@ -2005,7 +2077,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
|
||||
}
|
||||
} else {
|
||||
if (deferred) {
|
||||
dLog('rpc response', message.result);
|
||||
dLog('rpc response', message.result._);
|
||||
sentMessage.deferred.resolve(message.result);
|
||||
}
|
||||
if (sentMessage.isAPI) {
|
||||
@ -2077,14 +2149,13 @@ factory('MtpApiManager', function (AppConfigManager, MtpAuthorizer, MtpNetworker
|
||||
return $q.when(cachedNetworkers[dcID]);
|
||||
}
|
||||
|
||||
var deferred = $q.defer(),
|
||||
ak = 'dc' + dcID + '_auth_key',
|
||||
var akk = 'dc' + dcID + '_auth_key',
|
||||
ssk = 'dc' + dcID + '_server_salt';
|
||||
|
||||
AppConfigManager.get(ak, ssk).then(function (result) {
|
||||
return AppConfigManager.get(akk, ssk).then(function (result) {
|
||||
|
||||
if (cachedNetworkers[dcID] !== undefined) {
|
||||
return deferred.resolve(cachedNetworkers[dcID]);
|
||||
return cachedNetworkers[dcID];
|
||||
}
|
||||
|
||||
var authKeyHex = result[0],
|
||||
@ -2094,26 +2165,21 @@ factory('MtpApiManager', function (AppConfigManager, MtpAuthorizer, MtpNetworker
|
||||
var authKey = bytesFromHex(authKeyHex);
|
||||
var serverSalt = bytesFromHex(serverSaltHex);
|
||||
|
||||
return deferred.resolve(cachedNetworkers[dcID] = MtpNetworkerFactory.getNetworker(dcID, authKey, serverSalt));
|
||||
return cachedNetworkers[dcID] = MtpNetworkerFactory.getNetworker(dcID, authKey, serverSalt);
|
||||
}
|
||||
|
||||
MtpAuthorizer.auth(dcID).then(function (auth) {
|
||||
return MtpAuthorizer.auth(dcID).then(function (auth) {
|
||||
var storeObj = {};
|
||||
storeObj[ak] = bytesToHex(auth.authKey);
|
||||
storeObj[akk] = bytesToHex(auth.authKey);
|
||||
storeObj[ssk] = bytesToHex(auth.serverSalt);
|
||||
AppConfigManager.set(storeObj);
|
||||
|
||||
deferred.resolve(
|
||||
cachedNetworkers[dcID] = MtpNetworkerFactory.getNetworker(dcID, auth.authKey, auth.serverSalt)
|
||||
);
|
||||
return cachedNetworkers[dcID] = MtpNetworkerFactory.getNetworker(dcID, auth.authKey, auth.serverSalt);
|
||||
}, function (error) {
|
||||
dLog('Get networker error', error, error.stack);
|
||||
deferred.reject(error);
|
||||
return error;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
function mtpInvokeApi (method, params, options) {
|
||||
@ -2189,7 +2255,12 @@ factory('MtpApiManager', function (AppConfigManager, MtpAuthorizer, MtpNetworker
|
||||
});
|
||||
}
|
||||
|
||||
function getBaseDcID () {
|
||||
return baseDcID || false;
|
||||
}
|
||||
|
||||
return {
|
||||
getBaseDcID: getBaseDcID,
|
||||
getUserID: mtpGetUserID,
|
||||
invokeApi: mtpInvokeApi,
|
||||
setUserAuth: mtpSetUserAuth,
|
||||
@ -2205,22 +2276,28 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
|
||||
var cachedSavePromises = {};
|
||||
var cachedDownloadPromises = {};
|
||||
|
||||
var downloadPull = [];
|
||||
var downloadPulls = {};
|
||||
var downloadActive = 0;
|
||||
var downloadLimit = 5;
|
||||
|
||||
function downloadRequest(cb, activeDelta) {
|
||||
function downloadRequest(dcID, cb, activeDelta) {
|
||||
if (downloadPulls[dcID] === undefined) {
|
||||
downloadPulls[dcID] = [];
|
||||
}
|
||||
var downloadPull = downloadPulls[dcID];
|
||||
var deferred = $q.defer();
|
||||
downloadPull.push({cb: cb, deferred: deferred, activeDelta: activeDelta});
|
||||
downloadCheck();
|
||||
downloadCheck(dcID);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var index = 0;
|
||||
|
||||
function downloadCheck() {
|
||||
if (downloadActive >= downloadLimit || !downloadPull.length) {
|
||||
function downloadCheck(dcID) {
|
||||
var downloadPull = downloadPulls[dcID];
|
||||
|
||||
if (downloadActive >= downloadLimit || !downloadPull || !downloadPull.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2233,13 +2310,13 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
|
||||
data.cb()
|
||||
.then(function (result) {
|
||||
downloadActive -= activeDelta;
|
||||
downloadCheck();
|
||||
downloadCheck(dcID);
|
||||
|
||||
data.deferred.resolve(result);
|
||||
|
||||
}, function (error) {
|
||||
downloadActive -= activeDelta;
|
||||
downloadCheck();
|
||||
downloadCheck(dcID);
|
||||
|
||||
data.deferred.reject(error);
|
||||
})
|
||||
@ -2250,10 +2327,14 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
|
||||
return $q.when(cachedFS);
|
||||
}
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
$window.requestFileSystem = $window.requestFileSystem || $window.webkitRequestFileSystem;
|
||||
|
||||
if (!$window.requestFileSystem) {
|
||||
return $q.reject({type: 'FS_BROWSER_UNSUPPORTED', description: 'requestFileSystem not present'});
|
||||
}
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
$window.requestFileSystem($window.TEMPORARY, 5*1024*1024, function (fs) {
|
||||
cachedFS = fs;
|
||||
deferred.resolve();
|
||||
@ -2368,7 +2449,7 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
|
||||
},
|
||||
doDownload = function () {
|
||||
cachedFS.root.getFile(fileName, {create: true}, function(fileEntry) {
|
||||
var downloadPromise = downloadRequest(function () {
|
||||
var downloadPromise = downloadRequest(location.dc_id, function () {
|
||||
// dLog('next small promise');
|
||||
return MtpApiManager.invokeApi('upload.getFile', {
|
||||
location: angular.extend({}, location, {_: 'inputFileLocation'}),
|
||||
@ -2400,7 +2481,19 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
|
||||
}
|
||||
}, errorHandler);
|
||||
}, doDownload);
|
||||
}, errorHandler);
|
||||
}, function (error) {
|
||||
|
||||
downloadRequest(location.dc_id, function () {
|
||||
// dLog('next small promise');
|
||||
return MtpApiManager.invokeApi('upload.getFile', {
|
||||
location: angular.extend({}, location, {_: 'inputFileLocation'}),
|
||||
offset: 0,
|
||||
limit: 0
|
||||
}, {dcID: location.dc_id});
|
||||
}).then(function (result) {
|
||||
deferred.resolve('data:image/jpeg;base64,' + bytesToBase64(result.bytes))
|
||||
}, errorHandler);
|
||||
});
|
||||
|
||||
return cachedDownloadPromises[fileName] = deferred.promise;
|
||||
}
|
||||
@ -2415,6 +2508,7 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
|
||||
}
|
||||
|
||||
var deferred = $q.defer(),
|
||||
cacheFileWriter,
|
||||
errorHandler = function (error) {
|
||||
console.error(error);
|
||||
// dLog('fail');
|
||||
@ -2433,7 +2527,7 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
|
||||
for (var offset = 0; offset < size; offset += limit) {
|
||||
writeFileDeferred = $q.defer();
|
||||
(function (isFinal, offset, writeFileDeferred, writeFilePromise) {
|
||||
return downloadRequest(function () {
|
||||
return downloadRequest(dcID, function () {
|
||||
// dLog('next big promise');
|
||||
return MtpApiManager.invokeApi('upload.getFile', {
|
||||
location: location,
|
||||
|
@ -309,7 +309,7 @@ angular.module('myApp.services', [])
|
||||
var chatFull = angular.copy(fullChat),
|
||||
chat = getChat(id);
|
||||
|
||||
if (chatFull.participants._ == 'chatParticipants') {
|
||||
if (chatFull.participants && chatFull.participants._ == 'chatParticipants') {
|
||||
angular.forEach(chatFull.participants.participants, function(participant){
|
||||
participant.user = AppUsersManager.getUser(participant.user_id);
|
||||
participant.userPhoto = AppUsersManager.getUserPhoto(participant.user_id, 'User');
|
||||
@ -335,21 +335,10 @@ angular.module('myApp.services', [])
|
||||
scope.chatID = chatID;
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'partials/chat_modal.html?1',
|
||||
templateUrl: 'partials/chat_modal.html?2',
|
||||
controller: 'ChatModalController',
|
||||
windowClass: 'chat_modal_window',
|
||||
scope: scope,
|
||||
resolve: {
|
||||
fullChat: function () {
|
||||
return MtpApiManager.invokeApi('messages.getFullChat', {
|
||||
chat_id: chatID
|
||||
}).then(function (result) {
|
||||
saveApiChats(result.chats);
|
||||
AppUsersManager.saveApiUsers(result.users);
|
||||
return result.full_chat;
|
||||
})
|
||||
}
|
||||
}
|
||||
scope: scope
|
||||
});
|
||||
}
|
||||
|
||||
@ -1172,7 +1161,7 @@ angular.module('myApp.services', [])
|
||||
height = 100,
|
||||
thumbPhotoSize = choosePhotoSize(photo, width, height),
|
||||
thumb = {
|
||||
placeholder: 'img/placeholders/PhotoThumbConversation.jpg',
|
||||
placeholder: 'img/placeholders/PhotoThumbConversation.gif',
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
@ -1200,7 +1189,7 @@ angular.module('myApp.services', [])
|
||||
fullHeight = $($window).height() - 150,
|
||||
fullPhotoSize = choosePhotoSize(photo, fullWidth, fullHeight),
|
||||
full = {
|
||||
placeholder: 'img/placeholders/PhotoThumbModal.jpg',
|
||||
placeholder: 'img/placeholders/PhotoThumbModal.gif',
|
||||
width: fullWidth,
|
||||
height: fullHeight
|
||||
};
|
||||
@ -1272,7 +1261,7 @@ angular.module('myApp.services', [])
|
||||
height = 100,
|
||||
thumbPhotoSize = video.thumb,
|
||||
thumb = {
|
||||
placeholder: 'img/placeholders/VideoThumbConversation.jpg',
|
||||
placeholder: 'img/placeholders/VideoThumbConversation.gif',
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
@ -1299,7 +1288,7 @@ angular.module('myApp.services', [])
|
||||
fullHeight = $($window).height() - 150,
|
||||
fullPhotoSize = video,
|
||||
full = {
|
||||
placeholder: 'img/placeholders/VideoThumbModal.jpg',
|
||||
placeholder: 'img/placeholders/VideoThumbModal.gif',
|
||||
width: fullWidth,
|
||||
height: fullHeight,
|
||||
};
|
||||
@ -1865,7 +1854,10 @@ angular.module('myApp.services', [])
|
||||
var notificationsShown = [];
|
||||
var notificationsCount = 0;
|
||||
var peerSettings = {};
|
||||
var faviconEl = $('link[rel="icon"]');
|
||||
|
||||
var titleBackup = document.title,
|
||||
faviconBackup = faviconEl.attr('href'),
|
||||
titlePromise;
|
||||
|
||||
$rootScope.$watch('idle.isIDLE', function (newVal) {
|
||||
@ -1875,6 +1867,7 @@ angular.module('myApp.services', [])
|
||||
if (!newVal) {
|
||||
notificationsCount = 0;
|
||||
document.title = titleBackup;
|
||||
faviconEl.attr('href', faviconBackup);
|
||||
notificationsClear();
|
||||
} else {
|
||||
titleBackup = document.title;
|
||||
@ -1884,8 +1877,10 @@ angular.module('myApp.services', [])
|
||||
// dLog('check title', notificationsCount, time % 2000 > 1000);
|
||||
if (!notificationsCount || time % 2000 > 1000) {
|
||||
document.title = titleBackup;
|
||||
faviconEl.attr('href', faviconBackup);
|
||||
} else {
|
||||
document.title = notificationsCount + ' notifications';
|
||||
faviconEl.attr('href', 'favicon_unread.ico');
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
@ -8,13 +8,13 @@
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="chat_modal_image_wrap pull-left">
|
||||
<img class="chat_modal_image" my-load-thumb thumb="chatFull.thumb"/>
|
||||
<img class="chat_modal_image" src="img/blank.gif" my-load-thumb thumb="chatFull.thumb"/>
|
||||
</div>
|
||||
|
||||
<div class="chat_modal_info_wrap clearfix">
|
||||
<h4 class="chat_modal_header">{{chatFull.chat | chatTitle}}</h4>
|
||||
<p class="chat_modal_members_count" ng-if="chatFull.participants._ == 'chatParticipants'">
|
||||
<ng-pluralize count="chatFull.participants.participants.length"
|
||||
<h4 class="chat_modal_header" ng-bind-html="chatFull.chat.rTitle"></h4>
|
||||
<p class="chat_modal_members_count" ng-if="chatFull.chat.participants_count > 0">
|
||||
<ng-pluralize count="chatFull.chat.participants_count"
|
||||
when="{'0': 'No members', 'one': '1 member', 'other': '{} members'}">
|
||||
</ng-pluralize>
|
||||
</p>
|
||||
|
@ -4,13 +4,11 @@
|
||||
<div class="im_dialog_date">
|
||||
{{dialogMessage.date | dateOrTime}}
|
||||
</div>
|
||||
<span class="im_dialog_badge badge" ng-show="dialogMessage.unreadCount > 0">
|
||||
{{dialogMessage.unreadCount}}
|
||||
</span>
|
||||
<span class="im_dialog_badge badge" ng-show="dialogMessage.unreadCount > 0" ng-bind="dialogMessage.unreadCount"></span>
|
||||
</div>
|
||||
|
||||
<div class="im_dialog_photo pull-left">
|
||||
<img class="im_dialog_photo" src="img/blank.gif" my-load-thumb thumb="dialogMessage.peerPhoto" aaa="{{dialogMessage.peerPhoto.location}}" />
|
||||
<img class="im_dialog_photo" src="img/blank.gif" my-load-thumb thumb="dialogMessage.peerPhoto" />
|
||||
</div>
|
||||
|
||||
<div class="im_dialog_message_wrap">
|
||||
|
@ -86,7 +86,9 @@
|
||||
<i class="icon icon-document"></i>
|
||||
<div class="im_message_document_info">
|
||||
<div class="im_message_document_name_wrap">
|
||||
<span class="im_message_document_name">{{historyMessage.media.document.file_name}}</span>
|
||||
<span class="im_message_document_name">
|
||||
{{historyMessage.media.document.file_name}}
|
||||
</span>
|
||||
<span class="im_message_document_size" ng-if="!historyMessage.media.document.progress.enabled">
|
||||
{{historyMessage.media.document.size | formatSize}}
|
||||
</span>
|
||||
@ -100,7 +102,9 @@
|
||||
<div class="im_message_download_progress_wrap" ng-if="historyMessage.media.document.progress.enabled">
|
||||
<div class="progress tg_down_progress">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{historyMessage.media.document.progress.percent}}" aria-valuemin="0" aria-valuemax="100" style="width: {{historyMessage.media.document.progress.percent}}%">
|
||||
<span class="sr-only">{{historyMessage.media.document.progress.percent}}% Complete (success)</span>
|
||||
<span class="sr-only">
|
||||
{{historyMessage.media.document.progress.percent}}% Complete (success)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -115,15 +119,21 @@
|
||||
<a ng-click="openUser(historyMessage.media.user.id)" class="im_message_contact_photo pull-left" ng-if="historyMessage.media.user">
|
||||
<img class="im_message_contact_photo" src="img/blank.gif" my-load-thumb thumb="historyMessage.media.userPhoto"/>
|
||||
</a>
|
||||
<div class="im_message_contact_name"><span class="glyphicon glyphicon-user"></span> {{historyMessage.media.first_name}} {{historyMessage.media.last_name}}</div>
|
||||
<div class="im_message_contact_phone">{{historyMessage.media.phone_number}}</div>
|
||||
<div class="im_message_contact_name"><span class="glyphicon glyphicon-user"></span>
|
||||
{{historyMessage.media.first_name}} {{historyMessage.media.last_name}}
|
||||
</div>
|
||||
<div class="im_message_contact_phone">
|
||||
{{historyMessage.media.phone_number}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-switch-when="messageMediaPending" class="im_message_upload_file im_message_upload_{{historyMessage.media.type}}">
|
||||
<i class="icon icon-document"></i>
|
||||
<div class="im_message_document_info">
|
||||
<div class="im_message_document_name_wrap">
|
||||
<span class="im_message_document_name">{{historyMessage.media.file_name}}</span>
|
||||
<span class="im_message_document_name">
|
||||
{{historyMessage.media.file_name}}
|
||||
</span>
|
||||
<span class="im_message_document_size" ng-if="historyMessage.media.progress">
|
||||
{{historyMessage.media.progress | formatSizeProgress}}
|
||||
</span>
|
||||
@ -131,7 +141,9 @@
|
||||
<div class="im_message_download_progress_wrap">
|
||||
<div class="progress tg_down_progress">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{historyMessage.media.progress.percent}}" aria-valuemin="0" aria-valuemax="100" style="width: {{historyMessage.media.progress.percent}}%">
|
||||
<span class="sr-only">{{historyMessage.media.progress.percent}}% Complete (success)</span>
|
||||
<span class="sr-only">
|
||||
{{historyMessage.media.progress.percent}}% Complete (success)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
1
app/vendor/jquery/jquery.min.map
vendored
Normal file
1
app/vendor/jquery/jquery.min.map
vendored
Normal file
File diff suppressed because one or more lines are too long
8
app/vendor/zlib/gunzip.min.js.map
vendored
Normal file
8
app/vendor/zlib/gunzip.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
8
app/vendor/zlib/gzip.min.js.map
vendored
Normal file
8
app/vendor/zlib/gzip.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user