Browse Source

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
master
Igor Zhukov 11 years ago
parent
commit
ca781f6c86
  1. 49
      app/css/app.css
  2. BIN
      app/favicon_unread.ico
  3. BIN
      app/img/placeholders/PhotoThumbConversation.gif
  4. BIN
      app/img/placeholders/PhotoThumbModal.gif
  5. BIN
      app/img/placeholders/VideoThumbConversation.gif
  6. BIN
      app/img/placeholders/VideoThumbModal.gif
  7. 14
      app/index.html
  8. 2
      app/js/app.js
  9. 16
      app/js/controllers.js
  10. 32
      app/js/directives.js
  11. 9
      app/js/filters.js
  12. 200
      app/js/lib/mtproto.js
  13. 31
      app/js/services.js
  14. 8
      app/partials/chat_modal.html
  15. 6
      app/partials/dialog.html
  16. 24
      app/partials/message.html
  17. 1
      app/vendor/jquery/jquery.min.map
  18. 8
      app/vendor/zlib/gunzip.min.js.map
  19. 8
      app/vendor/zlib/gzip.min.js.map

49
app/css/app.css

@ -226,6 +226,7 @@ fieldset[disabled] .btn-tg.active { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -1104,7 +1106,6 @@ img.img_fullsize {
.chat_modal_window .modal-dialog {
max-width: 506px;
}
.chat_modal_wrap .modal-body {
padding: 23px 25px 15px;
}
@ -1254,3 +1255,31 @@ img.img_fullsize { @@ -1254,3 +1255,31 @@ img.img_fullsize {
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

BIN
app/img/placeholders/PhotoThumbConversation.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 B

BIN
app/img/placeholders/PhotoThumbModal.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 B

BIN
app/img/placeholders/VideoThumbConversation.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 B

BIN
app/img/placeholders/VideoThumbModal.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 B

14
app/index.html

@ -7,7 +7,7 @@ @@ -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 @@ @@ -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>

2
app/js/app.js

@ -25,7 +25,7 @@ angular.module('myApp', [ @@ -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++) {

16
app/js/controllers.js

@ -121,7 +121,8 @@ angular.module('myApp.controllers', []) @@ -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', []) @@ -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);
});
})

32
app/js/directives.js

@ -16,7 +16,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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));
});

9
app/js/filters.js

@ -52,7 +52,14 @@ angular.module('myApp.filters', []) @@ -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', []) @@ -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);
}
}])

200
app/js/lib/mtproto.js

@ -61,6 +61,40 @@ function bytesFromHex (hexString) { @@ -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) { @@ -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 @@ -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 @@ -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,13 +1782,14 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -1741,13 +1782,14 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
}
this.pendingAcks = [];
var self = this;
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);
self.processMessage(response.response, response.messageID, response.sessionID);
if (noResponseMsgs.length) {
$timeout(function () {
angular.forEach(noResponseMsgs, function (msgID) {
if (self.sentMessages[msgID]) {
var deferred = self.sentMessages[msgID].deferred;
@ -1755,8 +1797,9 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -1755,8 +1797,9 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
deferred.resolve();
}
});
}, 200);
}
self.cleanupSent();
});
});
};
@ -1833,7 +1876,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -2400,7 +2481,19 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
}
}, errorHandler);
}, doDownload);
}, 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) { @@ -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) { @@ -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,

31
app/js/services.js

@ -309,7 +309,7 @@ angular.module('myApp.services', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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', []) @@ -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
app/partials/chat_modal.html

@ -8,13 +8,13 @@ @@ -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>

6
app/partials/dialog.html

@ -4,13 +4,11 @@ @@ -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">

24
app/partials/message.html

@ -86,7 +86,9 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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

File diff suppressed because one or more lines are too long

8
app/vendor/zlib/gunzip.min.js.map vendored

File diff suppressed because one or more lines are too long

8
app/vendor/zlib/gzip.min.js.map vendored

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save