Telegram Web, preconfigured for usage in I2P.
http://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
849 lines
27 KiB
849 lines
27 KiB
/*! |
|
* Webogram v0.7 - messaging web application for MTProto |
|
* https://github.com/zhukov/webogram |
|
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> |
|
* https://github.com/zhukov/webogram/blob/master/LICENSE |
|
*/ |
|
|
|
angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) |
|
|
|
.factory('MtpApiManager', function (Storage, MtpAuthorizer, MtpNetworkerFactory, MtpSingleInstanceService, AppRuntimeManager, ErrorService, qSync, $rootScope, $q, WebPushApiManager, TelegramMeWebService) { |
|
var cachedNetworkers = {} |
|
var cachedUploadNetworkers = {} |
|
var cachedExportPromise = {} |
|
var baseDcID = false |
|
|
|
var telegramMeNotified |
|
|
|
MtpSingleInstanceService.start() |
|
|
|
Storage.get('dc').then(function (dcID) { |
|
if (dcID) { |
|
baseDcID = dcID |
|
} |
|
}) |
|
|
|
function telegramMeNotify (newValue) { |
|
if (telegramMeNotified !== newValue) { |
|
telegramMeNotified = newValue |
|
TelegramMeWebService.setAuthorized(telegramMeNotified) |
|
} |
|
} |
|
|
|
function mtpSetUserAuth (dcID, userAuth) { |
|
var fullUserAuth = angular.extend({dcID: dcID}, userAuth) |
|
Storage.set({ |
|
dc: dcID, |
|
user_auth: fullUserAuth |
|
}) |
|
telegramMeNotify(true) |
|
$rootScope.$broadcast('user_auth', fullUserAuth) |
|
|
|
baseDcID = dcID |
|
} |
|
|
|
function mtpLogOut () { |
|
var storageKeys = [] |
|
for (var dcID = 1; dcID <= 5; dcID++) { |
|
storageKeys.push('dc' + dcID + '_auth_key') |
|
} |
|
WebPushApiManager.forceUnsubscribe() |
|
return Storage.get(storageKeys).then(function (storageResult) { |
|
var logoutPromises = [] |
|
for (var i = 0; i < storageResult.length; i++) { |
|
if (storageResult[i]) { |
|
logoutPromises.push(mtpInvokeApi('auth.logOut', {}, {dcID: i + 1, ignoreErrors: true})) |
|
} |
|
} |
|
return $q.all(logoutPromises).then(function () { |
|
Storage.remove('dc', 'user_auth') |
|
baseDcID = false |
|
telegramMeNotify(false) |
|
return mtpClearStorage() |
|
}, function (error) { |
|
storageKeys.push('dc', 'user_auth') |
|
Storage.remove(storageKeys) |
|
baseDcID = false |
|
error.handled = true |
|
telegramMeNotify(false) |
|
return mtpClearStorage() |
|
}) |
|
}) |
|
} |
|
|
|
function mtpClearStorage () { |
|
var saveKeys = ['user_auth', 't_user_auth', 'dc', 't_dc'] |
|
for (var dcID = 1; dcID <= 5; dcID++) { |
|
saveKeys.push('dc' + dcID + '_auth_key') |
|
saveKeys.push('t_dc' + dcID + '_auth_key') |
|
} |
|
Storage.noPrefix() |
|
Storage.get(saveKeys).then(function (values) { |
|
Storage.clear().then(function () { |
|
var restoreObj = {} |
|
angular.forEach(saveKeys, function (key, i) { |
|
var value = values[i] |
|
if (value !== false && value !== undefined) { |
|
restoreObj[key] = value |
|
} |
|
}) |
|
Storage.noPrefix() |
|
return Storage.set(restoreObj) |
|
}) |
|
}) |
|
} |
|
|
|
function mtpGetNetworker (dcID, options) { |
|
options = options || {} |
|
|
|
var cache = (options.fileUpload || options.fileDownload) |
|
? cachedUploadNetworkers |
|
: cachedNetworkers |
|
if (!dcID) { |
|
throw new Exception('get Networker without dcID') |
|
} |
|
|
|
if (cache[dcID] !== undefined) { |
|
return qSync.when(cache[dcID]) |
|
} |
|
|
|
var akk = 'dc' + dcID + '_auth_key' |
|
var ssk = 'dc' + dcID + '_server_salt' |
|
|
|
return Storage.get(akk, ssk).then(function (result) { |
|
if (cache[dcID] !== undefined) { |
|
return cache[dcID] |
|
} |
|
|
|
var authKeyHex = result[0] |
|
var serverSaltHex = result[1] |
|
// console.log('ass', dcID, authKeyHex, serverSaltHex) |
|
if (authKeyHex && authKeyHex.length == 512) { |
|
if (!serverSaltHex || serverSaltHex.length != 16) { |
|
serverSaltHex = 'AAAAAAAAAAAAAAAA' |
|
} |
|
var authKey = bytesFromHex(authKeyHex) |
|
var serverSalt = bytesFromHex(serverSaltHex) |
|
|
|
return cache[dcID] = MtpNetworkerFactory.getNetworker(dcID, authKey, serverSalt, options) |
|
} |
|
|
|
if (!options.createNetworker) { |
|
return $q.reject({type: 'AUTH_KEY_EMPTY', code: 401}) |
|
} |
|
|
|
return MtpAuthorizer.auth(dcID).then(function (auth) { |
|
var storeObj = {} |
|
storeObj[akk] = bytesToHex(auth.authKey) |
|
storeObj[ssk] = bytesToHex(auth.serverSalt) |
|
Storage.set(storeObj) |
|
|
|
return cache[dcID] = MtpNetworkerFactory.getNetworker(dcID, auth.authKey, auth.serverSalt, options) |
|
}, function (error) { |
|
console.log('Get networker error', error, error.stack) |
|
return $q.reject(error) |
|
}) |
|
}) |
|
} |
|
|
|
function mtpInvokeApi (method, params, options) { |
|
options = options || {} |
|
|
|
var deferred = $q.defer() |
|
var rejectPromise = function (error) { |
|
if (!error) { |
|
error = {type: 'ERROR_EMPTY'} |
|
} else if (!angular.isObject(error)) { |
|
error = {message: error} |
|
} |
|
deferred.reject(error) |
|
if (options.ignoreErrors) { |
|
return |
|
} |
|
|
|
if (error.code == 406) { |
|
error.handled = true |
|
} |
|
|
|
if (!options.noErrorBox) { |
|
error.input = method |
|
error.stack = stack || (error.originalError && error.originalError.stack) || error.stack || (new Error()).stack |
|
setTimeout(function () { |
|
if (!error.handled) { |
|
if (error.code == 401) { |
|
mtpLogOut()['finally'](function () { |
|
if (location.protocol == 'http:' && |
|
!Config.Modes.http && |
|
Config.App.domains.indexOf(location.hostname) != -1) { |
|
location.href = location.href.replace(/^http:/, 'https:') |
|
} else { |
|
location.hash = '/login' |
|
AppRuntimeManager.reload() |
|
} |
|
}) |
|
} else { |
|
ErrorService.show({error: error}) |
|
} |
|
error.handled = true |
|
} |
|
}, 100) |
|
} |
|
}, |
|
dcID, |
|
networkerPromise |
|
|
|
var cachedNetworker |
|
var stack = (new Error()).stack || 'empty stack' |
|
var performRequest = function (networker) { |
|
return (cachedNetworker = networker).wrapApiCall(method, params, options).then( |
|
function (result) { |
|
deferred.resolve(result) |
|
}, |
|
function (error) { |
|
console.error(dT(), 'Error', error.code, error.type, baseDcID, dcID) |
|
if (error.code == 401 && baseDcID == dcID) { |
|
Storage.remove('dc', 'user_auth') |
|
telegramMeNotify(false) |
|
rejectPromise(error) |
|
} |
|
else if (error.code == 401 && baseDcID && dcID != baseDcID) { |
|
if (cachedExportPromise[dcID] === undefined) { |
|
var exportDeferred = $q.defer() |
|
|
|
mtpInvokeApi('auth.exportAuthorization', {dc_id: dcID}, {noErrorBox: true}).then(function (exportedAuth) { |
|
mtpInvokeApi('auth.importAuthorization', { |
|
id: exportedAuth.id, |
|
bytes: exportedAuth.bytes |
|
}, {dcID: dcID, noErrorBox: true}).then(function () { |
|
exportDeferred.resolve() |
|
}, function (e) { |
|
exportDeferred.reject(e) |
|
}) |
|
}, function (e) { |
|
exportDeferred.reject(e) |
|
}) |
|
|
|
cachedExportPromise[dcID] = exportDeferred.promise |
|
} |
|
|
|
cachedExportPromise[dcID].then(function () { |
|
(cachedNetworker = networker).wrapApiCall(method, params, options).then(function (result) { |
|
deferred.resolve(result) |
|
}, rejectPromise) |
|
}, rejectPromise) |
|
} |
|
else if (error.code == 303) { |
|
var newDcID = error.type.match(/^(PHONE_MIGRATE_|NETWORK_MIGRATE_|USER_MIGRATE_)(\d+)/)[2] |
|
if (newDcID != dcID) { |
|
if (options.dcID) { |
|
options.dcID = newDcID |
|
} else { |
|
Storage.set({dc: baseDcID = newDcID}) |
|
} |
|
|
|
mtpGetNetworker(newDcID, options).then(function (networker) { |
|
networker.wrapApiCall(method, params, options).then(function (result) { |
|
deferred.resolve(result) |
|
}, rejectPromise) |
|
}, rejectPromise) |
|
} |
|
} |
|
else if (!options.rawError && error.code == 420) { |
|
var waitTime = error.type.match(/^FLOOD_WAIT_(\d+)/)[1] || 10 |
|
if (waitTime > (options.timeout || 60)) { |
|
return rejectPromise(error) |
|
} |
|
setTimeout(function () { |
|
performRequest(cachedNetworker) |
|
}, waitTime * 1000) |
|
} |
|
else if (!options.rawError && (error.code == 500 || error.type == 'MSG_WAIT_FAILED')) { |
|
var now = tsNow() |
|
if (options.stopTime) { |
|
if (now >= options.stopTime) { |
|
return rejectPromise(error) |
|
} |
|
} else { |
|
options.stopTime = now + (options.timeout !== undefined ? options.timeout : 10) * 1000 |
|
} |
|
options.waitTime = options.waitTime ? Math.min(60, options.waitTime * 1.5) : 1 |
|
setTimeout(function () { |
|
performRequest(cachedNetworker) |
|
}, options.waitTime * 1000) |
|
}else { |
|
rejectPromise(error) |
|
} |
|
}) |
|
} |
|
|
|
if (dcID = (options.dcID || baseDcID)) { |
|
mtpGetNetworker(dcID, options).then(performRequest, rejectPromise) |
|
} else { |
|
Storage.get('dc').then(function (baseDcID) { |
|
mtpGetNetworker(dcID = baseDcID || 2, options).then(performRequest, rejectPromise) |
|
}) |
|
} |
|
|
|
return deferred.promise |
|
} |
|
|
|
function mtpGetUserID () { |
|
return Storage.get('user_auth').then(function (auth) { |
|
telegramMeNotify(auth && auth.id > 0 || false) |
|
return auth.id || 0 |
|
}) |
|
} |
|
|
|
function getBaseDcID () { |
|
return baseDcID || false |
|
} |
|
|
|
return { |
|
getBaseDcID: getBaseDcID, |
|
getUserID: mtpGetUserID, |
|
invokeApi: mtpInvokeApi, |
|
getNetworker: mtpGetNetworker, |
|
setUserAuth: mtpSetUserAuth, |
|
logOut: mtpLogOut |
|
} |
|
}) |
|
|
|
.factory('MtpApiFileManager', function (MtpApiManager, $q, qSync, FileManager, IdbFileStorage, TmpfsFileStorage, MemoryFileStorage, WebpManager) { |
|
var cachedFs = false |
|
var cachedFsPromise = false |
|
var cachedSavePromises = {} |
|
var cachedDownloadPromises = {} |
|
var cachedDownloads = {} |
|
|
|
var downloadPulls = {} |
|
var downloadActives = {} |
|
|
|
function downloadRequest (dcID, cb, activeDelta) { |
|
if (downloadPulls[dcID] === undefined) { |
|
downloadPulls[dcID] = [] |
|
downloadActives[dcID] = 0 |
|
} |
|
var downloadPull = downloadPulls[dcID] |
|
var deferred = $q.defer() |
|
downloadPull.push({cb: cb, deferred: deferred, activeDelta: activeDelta}) |
|
setZeroTimeout(function () { |
|
downloadCheck(dcID) |
|
}) |
|
|
|
return deferred.promise |
|
} |
|
|
|
var index = 0 |
|
|
|
function downloadCheck (dcID) { |
|
var downloadPull = downloadPulls[dcID] |
|
var downloadLimit = dcID == 'upload' ? 11 : 5 |
|
|
|
if (downloadActives[dcID] >= downloadLimit || !downloadPull || !downloadPull.length) { |
|
return false |
|
} |
|
|
|
var data = downloadPull.shift() |
|
var activeDelta = data.activeDelta || 1 |
|
|
|
downloadActives[dcID] += activeDelta |
|
|
|
var a = index++ |
|
data.cb() |
|
.then(function (result) { |
|
downloadActives[dcID] -= activeDelta |
|
downloadCheck(dcID) |
|
|
|
data.deferred.resolve(result) |
|
}, function (error) { |
|
downloadActives[dcID] -= activeDelta |
|
downloadCheck(dcID) |
|
|
|
data.deferred.reject(error) |
|
}) |
|
} |
|
|
|
function getFileName (location) { |
|
switch (location._) { |
|
case 'inputDocumentFileLocation': |
|
var fileName = (location.file_name || '').split('.', 2) |
|
var ext = fileName[1] || '' |
|
if (location.sticker && !WebpManager.isWebpSupported()) { |
|
ext += '.png' |
|
} |
|
var versionPart = location.version ? ('v' + location.version) : '' |
|
return fileName[0] + '_' + location.id + versionPart + '.' + ext |
|
|
|
default: |
|
if (!location.volume_id) { |
|
console.trace('Empty location', location) |
|
} |
|
var ext = 'jpg' |
|
if (location.sticker) { |
|
ext = WebpManager.isWebpSupported() ? 'webp' : 'png' |
|
} |
|
return location.volume_id + '_' + location.local_id + '_' + location.secret + '.' + ext |
|
} |
|
} |
|
|
|
function getTempFileName (file) { |
|
var size = file.size || -1 |
|
var random = nextRandomInt(0xFFFFFFFF) |
|
return '_temp' + random + '_' + size |
|
} |
|
|
|
function getCachedFile (location) { |
|
if (!location) { |
|
return false |
|
} |
|
var fileName = getFileName(location) |
|
|
|
return cachedDownloads[fileName] || false |
|
} |
|
|
|
function getFileStorage () { |
|
if (!Config.Modes.memory_only) { |
|
if (TmpfsFileStorage.isAvailable()) { |
|
return TmpfsFileStorage |
|
} |
|
if (IdbFileStorage.isAvailable()) { |
|
return IdbFileStorage |
|
} |
|
} |
|
return MemoryFileStorage |
|
} |
|
|
|
function saveSmallFile (location, bytes) { |
|
var fileName = getFileName(location) |
|
var mimeType = 'image/jpeg' |
|
|
|
if (!cachedSavePromises[fileName]) { |
|
cachedSavePromises[fileName] = getFileStorage().saveFile(fileName, bytes).then(function (blob) { |
|
return cachedDownloads[fileName] = blob |
|
}, function (error) { |
|
delete cachedSavePromises[fileName] |
|
}) |
|
} |
|
return cachedSavePromises[fileName] |
|
} |
|
|
|
function downloadSmallFile (location) { |
|
if (!FileManager.isAvailable()) { |
|
return $q.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'}) |
|
} |
|
var fileName = getFileName(location) |
|
var mimeType = location.sticker ? 'image/webp' : 'image/jpeg' |
|
var cachedPromise = cachedSavePromises[fileName] || cachedDownloadPromises[fileName] |
|
|
|
if (cachedPromise) { |
|
return cachedPromise |
|
} |
|
|
|
var fileStorage = getFileStorage() |
|
|
|
return cachedDownloadPromises[fileName] = fileStorage.getFile(fileName).then(function (blob) { |
|
return cachedDownloads[fileName] = blob |
|
}, function () { |
|
var downloadPromise = downloadRequest(location.dc_id, function () { |
|
var inputLocation = location |
|
if (!inputLocation._ || inputLocation._ == 'fileLocation') { |
|
inputLocation = angular.extend({}, location, {_: 'inputFileLocation'}) |
|
} |
|
// console.log('next small promise') |
|
return MtpApiManager.invokeApi('upload.getFile', { |
|
location: inputLocation, |
|
offset: 0, |
|
limit: 1024 * 1024 |
|
}, { |
|
dcID: location.dc_id, |
|
fileDownload: true, |
|
createNetworker: true, |
|
noErrorBox: true |
|
}) |
|
}) |
|
|
|
var processDownloaded = function (bytes) { |
|
if (!location.sticker || WebpManager.isWebpSupported()) { |
|
return qSync.when(bytes) |
|
} |
|
return WebpManager.getPngBlobFromWebp(bytes) |
|
} |
|
|
|
return fileStorage.getFileWriter(fileName, mimeType).then(function (fileWriter) { |
|
return downloadPromise.then(function (result) { |
|
return processDownloaded(result.bytes).then(function (proccessedResult) { |
|
return FileManager.write(fileWriter, proccessedResult).then(function () { |
|
return cachedDownloads[fileName] = fileWriter.finalize() |
|
}) |
|
}) |
|
}) |
|
}) |
|
}) |
|
} |
|
|
|
function getDownloadedFile (location, size) { |
|
var fileStorage = getFileStorage() |
|
var fileName = getFileName(location) |
|
|
|
return fileStorage.getFile(fileName, size) |
|
} |
|
|
|
function downloadFile (dcID, location, size, options) { |
|
if (!FileManager.isAvailable()) { |
|
return $q.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'}) |
|
} |
|
|
|
options = options || {} |
|
|
|
var processSticker = false |
|
if (location.sticker && !WebpManager.isWebpSupported()) { |
|
if (options.toFileEntry || size > 524288) { |
|
delete location.sticker |
|
} else { |
|
processSticker = true |
|
options.mime = 'image/png' |
|
} |
|
} |
|
|
|
// console.log(dT(), 'Dload file', dcID, location, size) |
|
var fileName = getFileName(location) |
|
var toFileEntry = options.toFileEntry || null |
|
var cachedPromise = cachedSavePromises[fileName] || cachedDownloadPromises[fileName] |
|
|
|
var fileStorage = getFileStorage() |
|
|
|
// console.log(dT(), 'fs', fileStorage.name, fileName, cachedPromise) |
|
|
|
if (cachedPromise) { |
|
if (toFileEntry) { |
|
return cachedPromise.then(function (blob) { |
|
return FileManager.copy(blob, toFileEntry) |
|
}) |
|
} |
|
return cachedPromise |
|
} |
|
|
|
var deferred = $q.defer() |
|
var canceled = false |
|
var resolved = false |
|
var mimeType = options.mime || 'image/jpeg', |
|
cacheFileWriter |
|
var errorHandler = function (error) { |
|
deferred.reject(error) |
|
errorHandler = angular.noop |
|
if (cacheFileWriter && |
|
(!error || error.type != 'DOWNLOAD_CANCELED')) { |
|
cacheFileWriter.truncate(0) |
|
} |
|
} |
|
|
|
fileStorage.getFile(fileName, size).then(function (blob) { |
|
if (toFileEntry) { |
|
FileManager.copy(blob, toFileEntry).then(function () { |
|
deferred.resolve() |
|
}, errorHandler) |
|
} else { |
|
deferred.resolve(cachedDownloads[fileName] = blob) |
|
} |
|
}, function () { |
|
var fileWriterPromise = toFileEntry ? FileManager.getFileWriter(toFileEntry) : fileStorage.getFileWriter(fileName, mimeType) |
|
|
|
var processDownloaded = function (bytes) { |
|
if (!processSticker) { |
|
return qSync.when(bytes) |
|
} |
|
return WebpManager.getPngBlobFromWebp(bytes) |
|
} |
|
|
|
fileWriterPromise.then(function (fileWriter) { |
|
cacheFileWriter = fileWriter |
|
var limit = 524288, |
|
offset |
|
var startOffset = 0 |
|
var writeFilePromise = $q.when(), |
|
writeFileDeferred |
|
if (fileWriter.length) { |
|
startOffset = fileWriter.length |
|
if (startOffset >= size) { |
|
if (toFileEntry) { |
|
deferred.resolve() |
|
} else { |
|
deferred.resolve(cachedDownloads[fileName] = fileWriter.finalize()) |
|
} |
|
return |
|
} |
|
fileWriter.seek(startOffset) |
|
deferred.notify({done: startOffset, total: size}) |
|
} |
|
for (offset = startOffset; offset < size; offset += limit) { |
|
writeFileDeferred = $q.defer() |
|
;(function (isFinal, offset, writeFileDeferred, writeFilePromise) { |
|
return downloadRequest(dcID, function () { |
|
if (canceled) { |
|
return $q.when() |
|
} |
|
return MtpApiManager.invokeApi('upload.getFile', { |
|
location: location, |
|
offset: offset, |
|
limit: limit |
|
}, { |
|
dcID: dcID, |
|
fileDownload: true, |
|
singleInRequest: window.safari !== undefined, |
|
createNetworker: true |
|
}) |
|
}, 2).then(function (result) { |
|
writeFilePromise.then(function () { |
|
if (canceled) { |
|
return $q.when() |
|
} |
|
return processDownloaded(result.bytes).then(function (processedResult) { |
|
return FileManager.write(fileWriter, processedResult).then(function () { |
|
writeFileDeferred.resolve() |
|
}, errorHandler).then(function () { |
|
if (isFinal) { |
|
resolved = true |
|
if (toFileEntry) { |
|
deferred.resolve() |
|
} else { |
|
deferred.resolve(cachedDownloads[fileName] = fileWriter.finalize()) |
|
} |
|
} else { |
|
deferred.notify({done: offset + limit, total: size}) |
|
} |
|
}) |
|
}) |
|
}) |
|
}) |
|
})(offset + limit >= size, offset, writeFileDeferred, writeFilePromise) |
|
writeFilePromise = writeFileDeferred.promise |
|
} |
|
}) |
|
}) |
|
|
|
deferred.promise.cancel = function () { |
|
if (!canceled && !resolved) { |
|
canceled = true |
|
delete cachedDownloadPromises[fileName] |
|
errorHandler({type: 'DOWNLOAD_CANCELED'}) |
|
} |
|
} |
|
|
|
if (!toFileEntry) { |
|
cachedDownloadPromises[fileName] = deferred.promise |
|
} |
|
|
|
return deferred.promise |
|
} |
|
|
|
function uploadFile (file) { |
|
var fileSize = file.size, |
|
isBigFile = fileSize >= 10485760, |
|
canceled = false, |
|
resolved = false, |
|
doneParts = 0, |
|
partSize = 262144, // 256 Kb |
|
activeDelta = 2 |
|
|
|
if (fileSize > 67108864) { |
|
partSize = 524288 |
|
activeDelta = 4 |
|
} |
|
else if (fileSize < 102400) { |
|
partSize = 32768 |
|
activeDelta = 1 |
|
} |
|
var totalParts = Math.ceil(fileSize / partSize) |
|
|
|
if (totalParts > 3000) { |
|
return $q.reject({type: 'FILE_TOO_BIG'}) |
|
} |
|
|
|
var fileID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)] |
|
var deferred = $q.defer() |
|
var errorHandler = function (error) { |
|
// console.error('Up Error', error) |
|
deferred.reject(error) |
|
canceled = true |
|
errorHandler = angular.noop |
|
}, |
|
part = 0, |
|
offset, |
|
resultInputFile = { |
|
_: isBigFile ? 'inputFileBig' : 'inputFile', |
|
id: fileID, |
|
parts: totalParts, |
|
name: file.name, |
|
md5_checksum: '' |
|
} |
|
|
|
for (offset = 0; offset < fileSize; offset += partSize) { |
|
(function (offset, part) { |
|
downloadRequest('upload', function () { |
|
var uploadDeferred = $q.defer() |
|
|
|
var reader = new FileReader() |
|
var blob = file.slice(offset, offset + partSize) |
|
|
|
reader.onloadend = function (e) { |
|
if (canceled) { |
|
uploadDeferred.reject() |
|
return |
|
} |
|
if (e.target.readyState != FileReader.DONE) { |
|
return |
|
} |
|
MtpApiManager.invokeApi(isBigFile ? 'upload.saveBigFilePart' : 'upload.saveFilePart', { |
|
file_id: fileID, |
|
file_part: part, |
|
file_total_parts: totalParts, |
|
bytes: e.target.result |
|
}, { |
|
startMaxLength: partSize + 256, |
|
fileUpload: true, |
|
singleInRequest: true |
|
}).then(function (result) { |
|
doneParts++ |
|
uploadDeferred.resolve() |
|
if (doneParts >= totalParts) { |
|
deferred.resolve(resultInputFile) |
|
resolved = true |
|
} else { |
|
console.log(dT(), 'Progress', doneParts * partSize / fileSize) |
|
deferred.notify({done: doneParts * partSize, total: fileSize}) |
|
} |
|
}, errorHandler) |
|
} |
|
|
|
reader.readAsArrayBuffer(blob) |
|
|
|
return uploadDeferred.promise |
|
}, activeDelta) |
|
})(offset, part++) |
|
} |
|
|
|
deferred.promise.cancel = function () { |
|
console.log('cancel upload', canceled, resolved) |
|
if (!canceled && !resolved) { |
|
canceled = true |
|
errorHandler({type: 'UPLOAD_CANCELED'}) |
|
} |
|
} |
|
|
|
return deferred.promise |
|
} |
|
|
|
return { |
|
getCachedFile: getCachedFile, |
|
getDownloadedFile: getDownloadedFile, |
|
downloadFile: downloadFile, |
|
downloadSmallFile: downloadSmallFile, |
|
saveSmallFile: saveSmallFile, |
|
uploadFile: uploadFile |
|
} |
|
}) |
|
|
|
.service('MtpSingleInstanceService', function (_, $rootScope, $compile, $timeout, $interval, $modalStack, Storage, AppRuntimeManager, IdleManager, ErrorService, MtpNetworkerFactory) { |
|
var instanceID = nextRandomInt(0xFFFFFFFF) |
|
var started = false |
|
var masterInstance = false |
|
var deactivatePromise = false |
|
var deactivated = false |
|
var initial = false |
|
|
|
function start () { |
|
if (!started && !Config.Navigator.mobile && !Config.Modes.packed) { |
|
started = true |
|
|
|
IdleManager.start() |
|
|
|
$rootScope.$watch('idle.isIDLE', checkInstance) |
|
$interval(checkInstance, 5000) |
|
checkInstance() |
|
|
|
try { |
|
$($window).on('beforeunload', clearInstance) |
|
} catch (e) {} |
|
} |
|
} |
|
|
|
function clearInstance () { |
|
if (masterInstance && !deactivated) { |
|
console.warn('clear master instance'); |
|
Storage.remove('xt_instance') |
|
} |
|
} |
|
|
|
function deactivateInstance () { |
|
if (masterInstance || deactivated) { |
|
return false |
|
} |
|
console.log(dT(), 'deactivate') |
|
deactivatePromise = false |
|
deactivated = true |
|
clearInstance() |
|
$modalStack.dismissAll() |
|
|
|
document.title = _('inactive_tab_title_raw') |
|
|
|
var inactivePageCompiled = $compile('<ng-include src="\'partials/desktop/inactive.html\'"></ng-include>') |
|
|
|
var scope = $rootScope.$new(true) |
|
scope.close = function () { |
|
AppRuntimeManager.close() |
|
} |
|
scope.reload = function () { |
|
AppRuntimeManager.reload() |
|
} |
|
inactivePageCompiled(scope, function (clonedElement) { |
|
$('.page_wrap').hide() |
|
$(clonedElement).appendTo($('body')) |
|
}) |
|
$rootScope.idle.deactivated = true |
|
} |
|
|
|
function checkInstance () { |
|
if (deactivated) { |
|
return false |
|
} |
|
var time = tsNow() |
|
var idle = $rootScope.idle && $rootScope.idle.isIDLE |
|
var newInstance = {id: instanceID, idle: idle, time: time} |
|
|
|
Storage.get('xt_instance').then(function (curInstance) { |
|
// console.log(dT(), 'check instance', newInstance, curInstance) |
|
if (!idle || |
|
!curInstance || |
|
curInstance.id == instanceID || |
|
curInstance.time < time - 20000) { |
|
Storage.set({xt_instance: newInstance}) |
|
if (!masterInstance) { |
|
MtpNetworkerFactory.startAll() |
|
if (!initial) { |
|
initial = true |
|
} else { |
|
console.warn(dT(), 'now master instance', newInstance) |
|
} |
|
masterInstance = true |
|
} |
|
if (deactivatePromise) { |
|
$timeout.cancel(deactivatePromise) |
|
deactivatePromise = false |
|
} |
|
} else { |
|
if (masterInstance) { |
|
MtpNetworkerFactory.stopAll() |
|
console.warn(dT(), 'now idle instance', newInstance) |
|
if (!deactivatePromise) { |
|
deactivatePromise = $timeout(deactivateInstance, 30000) |
|
} |
|
masterInstance = false |
|
} |
|
} |
|
}) |
|
} |
|
|
|
return { |
|
start: start |
|
} |
|
})
|
|
|