diff --git a/app/css/app.css b/app/css/app.css index 447d1598..e5cced09 100644 --- a/app/css/app.css +++ b/app/css/app.css @@ -3,12 +3,14 @@ html { display: none; background: #dee4e9 url(../img/bg_tile.png) 0 0 repeat; + overflow: visible; /*background-size: 300px 468px;*/ } body { color: #000; background: none; font: 12px/18px "Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, Verdana, sans-serif; + overflow: visible; /*-webkit-font-smoothing: antialiased;*/ } .font-light { @@ -71,6 +73,10 @@ input[type="number"]::-webkit-inner-spin-button { margin: 0; } +input[type="number"] { + -moz-appearance:textfield; +} + .btn-success { color: #ffffff; @@ -312,9 +318,7 @@ input[type="number"]::-webkit-inner-spin-button { .modal-backdrop { background: #111111; -} -.modal-backdrop.in { - opacity: 0.25; + opacity: 0.25 !important; } .modal.fade .modal-dialog { diff --git a/app/index.html b/app/index.html index aa155c5b..45b669f5 100644 --- a/app/index.html +++ b/app/index.html @@ -20,72 +20,6 @@ - -
@@ -93,6 +27,7 @@ + diff --git a/app/js/controllers.js b/app/js/controllers.js index ba9eecc4..9bc1522e 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -427,16 +427,18 @@ angular.module('myApp.controllers', []) } AppMessagesManager.getDialogs($scope.search.query, maxID).then(function (dialogsResult) { - offset += dialogsResult.dialogs.length; - maxID = dialogsResult.dialogs[dialogsResult.dialogs.length - 1].top_message; - hasMore = dialogsResult.count === null || offset < dialogsResult.count; + if (dialogsResult.dialogs.length) { + offset += dialogsResult.dialogs.length; + maxID = dialogsResult.dialogs[dialogsResult.dialogs.length - 1].top_message; + hasMore = dialogsResult.count === null || offset < dialogsResult.count; - angular.forEach(dialogsResult.dialogs, function (dialog) { - peersInDialogs[dialog.peerID] = true; - $scope.dialogs.push(AppMessagesManager.wrapForDialog(dialog.top_message, dialog.unread_count)); - }); + angular.forEach(dialogsResult.dialogs, function (dialog) { + peersInDialogs[dialog.peerID] = true; + $scope.dialogs.push(AppMessagesManager.wrapForDialog(dialog.top_message, dialog.unread_count)); + }); - $scope.$broadcast('ui_dialogs_append'); + $scope.$broadcast('ui_dialogs_append'); + } }); }; @@ -1081,6 +1083,10 @@ angular.module('myApp.controllers', []) }); }; + $scope.download = function () { + AppPhotosManager.downloadPhoto($scope.photoID); + }; + $scope.$on('history_delete', function (e, historyUpdate) { console.log(dT(), 'delete', historyUpdate); diff --git a/app/js/directives.js b/app/js/directives.js index d1fd8b98..a20ffd2b 100644 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -833,7 +833,7 @@ angular.module('myApp.directives', ['myApp.filters']) } } - var downloadPromise = MtpApiFileManager.downloadFile($scope.video.dc_id, inputLocation, $scope.video.size, null, {mime: 'video/mp4'}); + var downloadPromise = MtpApiFileManager.downloadFile($scope.video.dc_id, inputLocation, $scope.video.size, {mime: 'video/mp4'}); downloadPromise.then(function (url) { $scope.progress.enabled = false; diff --git a/app/js/init.js b/app/js/init.js new file mode 100644 index 00000000..91aae840 --- /dev/null +++ b/app/js/init.js @@ -0,0 +1,63 @@ +(function () { + + // Prevent click-jacking + try { + if (window.chrome && chrome.app && chrome.app.window || self == top) { + document.documentElement.style.display = 'block'; + } else { + top.location = self.location; + } + } catch (e) {console.error('CJ protection', e)}; + + window.safeConfirm = function (params, callback) { + if (typeof params === 'string') { + params = {message: params}; + } + var result = false + try { + result = confirm(params.message); + } catch (e) { + result = true; + } + setTimeout(function () {callback(result)}, 10); + }; + + if (!window.applicationCache || !window.addEventListener) { + return; + } + + var appCache = window.applicationCache, + declined = false, + updateTimeout = false, + scheduleUpdate = function (delay) { + clearTimeout(updateTimeout); + updateTimeout = setTimeout(function () { + try { + appCache.update(); + } catch (ex) { + console.log('appCache.update: ' + ex); + } + }, delay || 300000); + }, + attach = function () { + appCache.addEventListener('updateready', function(e) { + if (appCache.status == appCache.UPDATEREADY) { + if (!declined) { + safeConfirm({type: 'WEBOGRAM_UPDATED_RELOAD', message: 'A new version of Webogram is available. Load it?'}, function (result) { + if (result) { + window.location.reload(); + } else { + declined = true; + } + }); + scheduleUpdate(); + } + } + }, false); + appCache.addEventListener('noupdate', function () {scheduleUpdate()}, false); + appCache.addEventListener('error', function () {scheduleUpdate()}, false); + }; + + scheduleUpdate(3000); + window.addEventListener('load', attach); +})(); \ No newline at end of file diff --git a/app/js/lib/mtproto.js b/app/js/lib/mtproto.js index 7c6a68a0..e239f4d5 100644 --- a/app/js/lib/mtproto.js +++ b/app/js/lib/mtproto.js @@ -1005,12 +1005,12 @@ factory('MtpDcConfigurator', function () { var dcOptions = window._testMode ? [ {id: 1, host: '173.240.5.253', port: 80}, - {id: 2, host: '109.239.131.195', port: 80}, + {id: 2, host: '149.154.167.40', port: 80}, {id: 3, host: '174.140.142.5', port: 80} ] : [ {id: 1, host: '173.240.5.1', port: 80}, - {id: 2, host: '109.239.131.193', port: 80}, + {id: 2, host: '149.154.167.5', port: 80}, {id: 3, host: '174.140.142.6', port: 80}, {id: 4, host: '31.210.235.12', port: 80}, {id: 5, host: '116.51.22.2', port: 80}, @@ -2832,7 +2832,7 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { deferred.reject(); }; - if (false) { // is file bytes + if (bytes instanceof Blob) { // is file bytes fileWriter.write(bytes); } else { fileWriter.write(new Blob([bytesToArrayBuffer(bytes)])); @@ -2841,6 +2841,23 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { return deferred.promise; } + function fileCopyTo (fromFileEntry, toFileEntry) { + var deferred = $q.defer(); + + toFileEntry.createWriter(function (fileWriter) { + fileWriteBytes(fileWriter, fromFileEntry).then(function () { + deferred.resolve(fileWriter); + }, function (e) { + fileWriter.truncate(0); + deferred.reject(e); + }); + }, function (e) { + deferred.reject(e); + }); + + return deferred.promise; + } + function getFileName(location) { switch (location._) { case 'inputVideoFileLocation': @@ -2982,14 +2999,15 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { return cachedDownloadPromises[fileName] = deferred.promise; } - function downloadFile (dcID, location, size, fileEntry, options) { + function downloadFile (dcID, location, size, options) { options = options || {}; console.log(dT(), 'Dload file', dcID, location, size); var fileName = getFileName(location), + toFileEntry = options.toFileEntry || null, cachedPromise = cachedSavePromises[fileName] || cachedDownloadPromises[fileName]; - if (cachedPromise) { + if (!toFileEntry && cachedPromise) { return cachedPromise; } @@ -3049,7 +3067,11 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { if (isFinal) { // console.timeEnd(fileName + ' ' + (size / 1024)); resolved = true; - deferred.resolve(cachedDownloads[fileName] = fileEntry.toURL(options.mime || 'image/jpeg')); + if (toFileEntry) { + deferred.resolve(); + } else { + deferred.resolve(cachedDownloads[fileName] = fileEntry.toURL(options.mime || 'image/jpeg')); + } } else { // console.log('notify', {done: offset + limit, total: size}); deferred.notify({done: offset + limit, total: size}); @@ -3070,90 +3092,104 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { }; - if (fileEntry) { - saveToFileEntry(fileEntry); - } else { - requestFS().then(function () { - cachedFs.root.getFile(fileName, {create: false}, function(fileEntry) { - fileEntry.file(function(file) { - // console.log(dT(), 'Check size', file.size, size); - if (file.size >= size/* && false*/) { - resolved = true; + requestFS().then(function () { + cachedFs.root.getFile(fileName, {create: false}, function(fileEntry) { + fileEntry.file(function(file) { + // console.log(dT(), 'Check size', file.size, size); + if (file.size >= size/* && false*/) { + resolved = true; + if (toFileEntry) { + fileCopyTo(file, toFileEntry).then(function () { + deferred.resolve(); + }) + } else { deferred.resolve(cachedDownloads[fileName] = fileEntry.toURL()); + } + } else { + // setTimeout(function () { + console.log('File bad size', file, size); + if (toFileEntry) { + saveToFileEntry(toFileEntry); } else { - // setTimeout(function () { - console.log('File bad size', file, size); cachedFs.root.getFile(fileName, {create: true}, saveToFileEntry, errorHandler) - // }, 10000); } - }, errorHandler); - }, function () { - cachedFs.root.getFile(fileName, {create: true}, saveToFileEntry, errorHandler) - }); + // }, 10000); + } + }, errorHandler); }, function () { + if (toFileEntry) { + saveToFileEntry(toFileEntry); + } else { + cachedFs.root.getFile(fileName, {create: true}, saveToFileEntry, errorHandler) + } + }); + }, function () { - var blobParts = []; - var limit = size > 30400 ? 524288 : 4096; - var writeBlobPromise = $q.when(), - writeBlobDeferred; - for (var offset = 0; offset < size; offset += limit) { - writeBlobDeferred = $q.defer(); - (function (isFinal, offset, writeBlobDeferred, writeBlobPromise) { - return downloadRequest(dcID, function () { + if (toFileEntry) { + return saveToFileEntry(toFileEntry); + } + + var blobParts = []; + var limit = size > 30400 ? 524288 : 4096; + var writeBlobPromise = $q.when(), + writeBlobDeferred; + for (var offset = 0; offset < size; offset += limit) { + writeBlobDeferred = $q.defer(); + (function (isFinal, offset, writeBlobDeferred, writeBlobPromise) { + return downloadRequest(dcID, function () { + if (canceled) { + return $q.when(); + } + return MtpApiManager.invokeApi('upload.getFile', { + location: location, + offset: offset, + limit: limit + }, { + dcID: dcID, + fileDownload: true, + createNetworker: true + }); + }, 6).then(function (result) { + writeBlobPromise.then(function () { if (canceled) { return $q.when(); } - return MtpApiManager.invokeApi('upload.getFile', { - location: location, - offset: offset, - limit: limit - }, { - dcID: dcID, - fileDownload: true, - createNetworker: true - }); - }, 6).then(function (result) { - writeBlobPromise.then(function () { - if (canceled) { - return $q.when(); - } - try { - blobParts.push(bytesToArrayBuffer(result.bytes)); - writeBlobDeferred.resolve(); - - if (isFinal) { - try { - var blob = new Blob(blobParts, {type: options.mime || 'image/jpeg'}); - } catch (e) { - window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder; - var bb = new BlobBuilder; - angular.forEach(blobParts, function(blobPart) { - bb.append(blobPart); - }); - var blob = bb.getBlob(options.mime || 'image/jpeg'); - } + try { + blobParts.push(bytesToArrayBuffer(result.bytes)); + writeBlobDeferred.resolve(); + + if (isFinal) { + try { + var blob = new Blob(blobParts, {type: options.mime || 'image/jpeg'}); + } catch (e) { + window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder; + var bb = new BlobBuilder; + angular.forEach(blobParts, function(blobPart) { + bb.append(blobPart); + }); + var blob = bb.getBlob(options.mime || 'image/jpeg'); + } - window.URL = window.URL || window.webkitURL; - resolved = true; - deferred.resolve(cachedDownloads[fileName] = URL.createObjectURL(blob)); - } else { - deferred.notify({done: offset + limit, total: size}); - }; - } catch (e) { - errorHandler(e); - } - }, errorHandler); + window.URL = window.URL || window.webkitURL; + resolved = true; + deferred.resolve(cachedDownloads[fileName] = URL.createObjectURL(blob)); + } else { + deferred.notify({done: offset + limit, total: size}); + }; + } catch (e) { + errorHandler(e); + } + }, errorHandler); - }); + }); - })(offset + limit >= size, offset, writeBlobDeferred, writeBlobPromise); + })(offset + limit >= size, offset, writeBlobDeferred, writeBlobPromise); - writeBlobPromise = writeBlobDeferred.promise; + writeBlobPromise = writeBlobDeferred.promise; - } + } - }); - } + }); deferred.promise.cancel = function () { if (!canceled && !resolved) { @@ -3163,37 +3199,12 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) { } } - return cachedDownloadPromises[fileName] = deferred.promise; - } - - function writeFile (file) { - console.log(dT(), 'Write file', file); - var fileName = getTempFileName(file); - - var deferred = $q.defer(), - cacheFileWriter, - errorHandler = function (error) { - console.log('fail'); - deferred.reject(error); - if (cacheFileWriter) cacheFileWriter.truncate(0); - errorHandler = angular.noop; - }; + if (!toFileEntry) { + cachedDownloadPromises[fileName] = deferred.promise; + } - requestFS().then(function () { - cachedFs.root.getFile(fileName, {create: false}, function(fileEntry) { - deferred.resolve(fileEntry); - }, function () { - cachedFs.root.getFile(fileName, {create: true}, function(fileEntry) { - fileEntry.createWriter(function (fileWriter) { - cacheFileWriter = fileWriter; - fileWriteBytes(fileWriter, file).then(function () { - deferred.resolve(fileEntry); - }, errorHandler); - }, errorHandler); - }); - }); - }); - }; + return deferred.promise; + } function uploadFile (file) { var fileSize = file.size, diff --git a/app/js/services.js b/app/js/services.js index a89b3296..db952450 100644 --- a/app/js/services.js +++ b/app/js/services.js @@ -1975,7 +1975,7 @@ angular.module('myApp.services', []) } }) -.service('AppPhotosManager', function ($modal, $window, $rootScope, MtpApiFileManager, AppUsersManager) { +.service('AppPhotosManager', function ($modal, $window, $timeout, $rootScope, MtpApiFileManager, AppUsersManager) { var photos = {}; function savePhoto (apiPhoto) { @@ -2123,6 +2123,65 @@ angular.module('myApp.services', []) }); } + function downloadPhoto (photoID) { + var photo = photos[photoID], + ext = 'jpg', + mimeType = 'image/jpeg', + fileName = 'photo' + photoID + '.' + ext, + fullWidth = $(window).width() - 36, + fullHeight = $($window).height() - 150, + fullPhotoSize = choosePhotoSize(photo, fullWidth, fullHeight), + inputFileLocation = { + _: 'inputFileLocation', + volume_id: fullPhotoSize.location.volume_id, + local_id: fullPhotoSize.location.local_id, + secret: fullPhotoSize.location.secret + }; + + if (window.chrome && chrome.fileSystem && chrome.fileSystem.chooseEntry) { + + chrome.fileSystem.chooseEntry({ + type: 'saveFile', + suggestedName: fileName, + accepts: [{ + mimeTypes: [mimeType], + extensions: [ext] + }] + }, function (writableFileEntry) { + var downloadPromise = MtpApiFileManager.downloadFile(fullPhotoSize.location.dc_id, inputFileLocation, fullPhotoSize.size, { + mime: mimeType, + toFileEntry: writableFileEntry + }); + downloadPromise.then(function (url) { + console.log('file save done'); + }, function (e) { + console.log('photo download failed', e); + }); + + }); + } else { + var downloadPromise = MtpApiFileManager.downloadFile(fullPhotoSize.location.dc_id, inputFileLocation, fullPhotoSize.size, {mime: mimeType}); + + downloadPromise.then(function (url) { + + var a = $('Download') + .css({position: 'absolute', top: 1, left: 1}) + .attr('href', url) + .attr('target', '_blank') + .attr('download', fileName) + .appendTo('body'); + + a[0].dataset.downloadurl = [mimeType, fileName, url].join(':'); + a[0].click(); + $timeout(function () { + a.remove(); + }, 100); + }, function (e) { + console.log('photo download failed', e); + }); + } + }; + $rootScope.openPhoto = openPhoto; @@ -2131,7 +2190,8 @@ angular.module('myApp.services', []) preloadPhoto: preloadPhoto, wrapForHistory: wrapForHistory, wrapForFull: wrapForFull, - openPhoto: openPhoto + openPhoto: openPhoto, + downloadPhoto: downloadPhoto } }) @@ -2263,7 +2323,10 @@ angular.module('myApp.services', []) extensions: [ext] }] }, function (writableFileEntry) { - var downloadPromise = MtpApiFileManager.downloadFile(video.dc_id, inputFileLocation, video.size, writableFileEntry, {mime: mimeType}); + var downloadPromise = MtpApiFileManager.downloadFile(video.dc_id, inputFileLocation, video.size, { + mime: mimeType, + toFileEntry: writableFileEntry + }); downloadPromise.then(function (url) { delete historyVideo.progress; console.log('file save done'); @@ -2275,7 +2338,7 @@ angular.module('myApp.services', []) historyVideo.progress.cancel = downloadPromise.cancel; }); } else { - var downloadPromise = MtpApiFileManager.downloadFile(video.dc_id, inputFileLocation, video.size, null, {mime: mimeType}); + var downloadPromise = MtpApiFileManager.downloadFile(video.dc_id, inputFileLocation, video.size, {mime: mimeType}); downloadPromise.then(function (url) { delete historyVideo.progress; @@ -2404,7 +2467,10 @@ angular.module('myApp.services', []) extensions: [ext] }] }, function (writableFileEntry) { - var downloadPromise = MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, writableFileEntry, {mime: doc.mime_type}); + var downloadPromise = MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, { + mime: doc.mime_type, + toFileEntry: writableFileEntry + }); downloadPromise.then(function (url) { delete historyDoc.progress; @@ -2417,7 +2483,7 @@ angular.module('myApp.services', []) historyDoc.progress.cancel = downloadPromise.cancel; }); } else { - var downloadPromise = MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, null, {mime: doc.mime_type}); + var downloadPromise = MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, {mime: doc.mime_type}); downloadPromise.then(function (url) { delete historyDoc.progress; @@ -2496,7 +2562,7 @@ angular.module('myApp.services', []) $rootScope.$broadcast('history_update'); } - var downloadPromise = MtpApiFileManager.downloadFile(audio.dc_id, inputFileLocation, audio.size, null, {mime: 'audio/ogg'}); + var downloadPromise = MtpApiFileManager.downloadFile(audio.dc_id, inputFileLocation, audio.size, {mime: 'audio/ogg'}); downloadPromise.then(function (url) { delete historyAudio.progress; diff --git a/app/manifest.json b/app/manifest.json index 319cfbf5..bd86a691 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -12,7 +12,8 @@ "notifications", "webview", {"fileSystem": ["write"]}, - "storage" + "storage", + "unlimitedStorage" ], "icons": { "16": "img/icons/icon16.png", diff --git a/app/partials/head.html b/app/partials/head.html index 643ea79b..b066a6cd 100644 --- a/app/partials/head.html +++ b/app/partials/head.html @@ -13,15 +13,15 @@ -