From c2aa9c571cec62bd049903a1e93a945b7e275736 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 29 Mar 2018 00:40:42 +0400 Subject: [PATCH] Follow secure file upload/download progress. --- .../passport/passport_edit_identity_box.cpp | 82 ++++-- .../passport/passport_edit_identity_box.h | 2 + .../passport/passport_form_controller.cpp | 269 +++++++++++++++--- .../passport/passport_form_controller.h | 36 ++- Telegram/SourceFiles/storage/file_upload.cpp | 10 +- Telegram/SourceFiles/storage/file_upload.h | 16 +- 6 files changed, 334 insertions(+), 81 deletions(-) diff --git a/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp b/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp index 947ad3a23f..32af478334 100644 --- a/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp +++ b/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp @@ -27,6 +27,11 @@ public: const QString &description); void setImage(const QImage &image); + void setDescription(const QString &description); + + rpl::producer<> deleteClicks() const { + return _delete->clicks(); + } protected: int resizeGetHeight(int newWidth) override; @@ -54,13 +59,11 @@ ScanButton::ScanButton( , _title( st::semiboldTextStyle, title, - Ui::NameTextOptions(), - st::boxWideWidth / 2) + Ui::NameTextOptions()) , _description( st::defaultTextStyle, description, - Ui::NameTextOptions(), - st::boxWideWidth / 2) + Ui::NameTextOptions()) , _delete(this, st::passportRowCheckbox) { } @@ -69,10 +72,18 @@ void ScanButton::setImage(const QImage &image) { update(); } +void ScanButton::setDescription(const QString &description) { + _description.setText( + st::defaultTextStyle, + description, + Ui::NameTextOptions()); + update(); +} + int ScanButton::resizeGetHeight(int newWidth) { const auto availableWidth = countAvailableWidth(newWidth); - _titleHeight = _title.countHeight(availableWidth); - _descriptionHeight = _description.countHeight(availableWidth); + _titleHeight = st::semiboldFont->height; + _descriptionHeight = st::normalFont->height; const auto result = st::passportRowPadding.top() + _titleHeight + st::passportRowSkip @@ -122,10 +133,10 @@ void ScanButton::paintEvent(QPaintEvent *e) { left += size + st::passportRowPadding.left(); availableWidth -= size + st::passportRowPadding.left(); - _title.drawLeft(p, left, top, availableWidth, width()); + _title.drawLeftElided(p, left, top, availableWidth, width()); top += _titleHeight + st::passportRowSkip; - _description.drawLeft(p, left, top, availableWidth, width()); + _description.drawLeftElided(p, left, top, availableWidth, width()); top += _descriptionHeight + st::passportRowPadding.bottom(); } @@ -155,21 +166,18 @@ void IdentityBox::prepare() { setTitle(langFactory(lng_passport_identity_title)); auto index = 0; - auto height = st::contactPadding.top(); for (const auto &scan : _files) { - _scans.push_back(object_ptr(this, QString("Scan %1").arg(++index), scan.date)); + _scans.push_back(object_ptr( + this, + QString("Scan %1").arg(++index), // #TODO langs + scan.status)); _scans.back()->setImage(scan.thumb); _scans.back()->resizeToWidth(st::boxWideWidth); - height += _scans.back()->height(); + _scans.back()->deleteClicks( + ) | rpl::start_with_next([=] { + _controller->deleteScan(_fieldIndex, index - 1); + }, lifetime()); } - height += st::contactPadding.top() - + _uploadScan->height() - + st::contactSkip - + _name->height() - + st::contactSkip - + _surname->height() - + st::contactPadding.bottom() - + st::boxPadding.bottom(); addButton(langFactory(lng_settings_save), [=] { save(); @@ -185,7 +193,23 @@ void IdentityBox::prepare() { _uploadScan->addClickHandler([=] { chooseScan(); }); - setDimensions(st::boxWideWidth, height); + setDimensions(st::boxWideWidth, countHeight()); +} + +int IdentityBox::countHeight() const { + auto height = st::contactPadding.top(); + for (const auto &scan : _scans) { + height += scan->height(); + } + height += st::contactPadding.top() + + _uploadScan->height() + + st::contactSkip + + _name->height() + + st::contactSkip + + _surname->height() + + st::contactPadding.bottom() + + st::boxPadding.bottom(); + return height; } void IdentityBox::updateScan(ScanInfo &&info) { @@ -194,8 +218,21 @@ void IdentityBox::updateScan(ScanInfo &&info) { }); if (i != _files.end()) { *i = info; + _scans[i - _files.begin()]->setDescription(i->status); _scans[i - _files.begin()]->setImage(i->thumb); + } else { + _files.push_back(std::move(info)); + _scans.push_back(object_ptr( + this, + QString("Scan %1").arg(_files.size()), + _files.back().status)); + _scans.back()->setImage(_files.back().thumb); + _scans.back()->resizeToWidth(st::boxWideWidth); + _scans.back()->show(); + updateControlsPosition(); + setDimensions(st::boxWideWidth, countHeight()); } + update(); } void IdentityBox::setInnerFocus() { @@ -211,6 +248,10 @@ void IdentityBox::resizeEvent(QResizeEvent *e) { _name->height()); _surname->resize(_name->width(), _surname->height()); + updateControlsPosition(); +} + +void IdentityBox::updateControlsPosition() { auto top = st::contactPadding.top(); for (const auto &scan : _scans) { scan->moveToLeft(0, top); @@ -253,7 +294,6 @@ void IdentityBox::encryptScan(const QString &path) { } void IdentityBox::encryptScanContent(QByteArray &&content) { - _uploadScan->hide(); _controller->uploadScan(_fieldIndex, std::move(content)); } diff --git a/Telegram/SourceFiles/passport/passport_edit_identity_box.h b/Telegram/SourceFiles/passport/passport_edit_identity_box.h index 052170593d..b5277e3451 100644 --- a/Telegram/SourceFiles/passport/passport_edit_identity_box.h +++ b/Telegram/SourceFiles/passport/passport_edit_identity_box.h @@ -45,6 +45,8 @@ private: void encryptScan(const QString &path); void encryptScanContent(QByteArray &&content); void updateScan(ScanInfo &&info); + int countHeight() const; + void updateControlsPosition(); void save(); not_null _controller; diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index a8c6b25877..7eb85c23d0 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -20,6 +20,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/file_download.h" namespace Passport { +namespace { + +QImage ReadImage(bytes::const_span buffer) { + return App::readImage(QByteArray::fromRawData( + reinterpret_cast(buffer.data()), + buffer.size())); +} + +} // namespace FormRequest::FormRequest( UserId botId, @@ -203,18 +212,45 @@ QString FormController::passwordHint() const { return _password.hint; } -void FormController::uploadScan(int index, QByteArray &&content) { +void FormController::uploadScan(int fieldIndex, QByteArray &&content) { Expects(_editBox != nullptr); - Expects(index >= 0 && index < _form.fields.size()); + Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size()); - auto &field = _form.fields[index]; + auto &field = _form.fields[fieldIndex]; if (field.secret.empty()) { field.secret = GenerateSecretBytes(); } + auto fileIndex = int(field.filesInEdit.size()); + field.filesInEdit.emplace_back( + File(), + nullptr); + const auto fileId = rand_value(); + auto &file = field.filesInEdit.back(); + file.fields.size = content.size(); + file.fields.id = fileId; + file.fields.dcId = MTP::maindc(); + file.fields.image = ReadImage(bytes::make_span(content)); + file.fields.downloadOffset = file.fields.size; + _scanUpdated.fire(collectScanInfo(file)); + + encryptScan(fieldIndex, fileIndex, std::move(content)); +} + +void FormController::encryptScan( + int fieldIndex, + int fileIndex, + QByteArray &&content) { + Expects(_editBox != nullptr); + Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size()); + Expects(fileIndex >= 0 + && fileIndex < _form.fields[fieldIndex].filesInEdit.size()); + + auto &field = _form.fields[fieldIndex]; const auto weak = _editBox; crl::async([ =, + fileId = field.filesInEdit[fileIndex].fields.id, bytes = std::move(content), valueSecret = field.secret ] { @@ -222,7 +258,7 @@ void FormController::uploadScan(int index, QByteArray &&content) { bytes::make_span(bytes), valueSecret); auto result = UploadedScan(); - result.fileId = rand_value(); + result.fileId = fileId; result.hash = std::move(data.hash); result.bytes = std::move(data.bytes); result.md5checksum.resize(32); @@ -232,38 +268,60 @@ void FormController::uploadScan(int index, QByteArray &&content) { result.md5checksum.data()); crl::on_main([=, encrypted = std::move(result)]() mutable { if (weak) { - uploadEncryptedScan(index, std::move(encrypted)); + uploadEncryptedScan( + fieldIndex, + fileIndex, + std::move(encrypted)); } }); }); } +void FormController::deleteScan( + int fieldIndex, + int fileIndex) { + Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size()); + Expects(fileIndex >= 0 + && fileIndex < _form.fields[fieldIndex].filesInEdit.size()); + + auto &file = _form.fields[fieldIndex].filesInEdit[fileIndex]; + file.deleted = !file.deleted; + _scanUpdated.fire(collectScanInfo(file)); +} + void FormController::subscribeToUploader() { if (_uploaderSubscriptions) { return; } + + using namespace Storage; Auth().uploader().secureReady( - ) | rpl::start_with_next([=](const Storage::UploadedSecure &data) { - scanUploaded(data); + ) | rpl::start_with_next([=](const UploadSecureDone &data) { + scanUploadDone(data); }, _uploaderSubscriptions); Auth().uploader().secureProgress( - ) | rpl::start_with_next([=](const FullMsgId &fullId) { + ) | rpl::start_with_next([=](const UploadSecureProgress &data) { + scanUploadProgress(data); }, _uploaderSubscriptions); Auth().uploader().secureFailed( ) | rpl::start_with_next([=](const FullMsgId &fullId) { + scanUploadFail(fullId); }, _uploaderSubscriptions); } -void FormController::uploadEncryptedScan(int index, UploadedScan &&data) { +void FormController::uploadEncryptedScan( + int fieldIndex, + int fileIndex, + UploadedScan &&data) { Expects(_editBox != nullptr); - Expects(index >= 0 && index < _form.fields.size()); + Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size()); + Expects(fileIndex >= 0 + && fileIndex < _form.fields[fieldIndex].filesInEdit.size()); subscribeToUploader(); - _form.fields[index].filesInEdit.emplace_back( - File(), - std::make_unique(std::move(data))); - auto &file = _form.fields[index].filesInEdit.back(); + auto &file = _form.fields[fieldIndex].filesInEdit[fileIndex]; + file.uploaded = std::make_unique(std::move(data)); auto uploaded = std::make_shared( TaskId(), @@ -282,17 +340,37 @@ void FormController::uploadEncryptedScan(int index, UploadedScan &&data) { Auth().uploader().upload(file.uploaded->fullId, std::move(uploaded)); } -void FormController::scanUploaded(const Storage::UploadedSecure &data) { - if (const auto edit = findEditFile(data.fullId)) { - Assert(edit->uploaded != nullptr); +void FormController::scanUploadDone(const Storage::UploadSecureDone &data) { + if (const auto file = findEditFile(data.fullId)) { + Assert(file->uploaded != nullptr); + Assert(file->uploaded->fileId == data.fileId); - edit->fields.id = edit->uploaded->fileId = data.fileId; - edit->fields.size = edit->uploaded->bytes.size(); - edit->fields.dcId = MTP::maindc(); - edit->uploaded->partsCount = data.partsCount; - edit->fields.bytes = std::move(edit->uploaded->bytes); - edit->fields.fileHash = std::move(edit->uploaded->hash); - edit->uploaded->fullId = FullMsgId(); + file->uploaded->partsCount = data.partsCount; + file->fields.fileHash = std::move(file->uploaded->hash); + file->uploaded->fullId = FullMsgId(); + + _scanUpdated.fire(collectScanInfo(*file)); + } +} + +void FormController::scanUploadProgress( + const Storage::UploadSecureProgress &data) { + if (const auto file = findEditFile(data.fullId)) { + Assert(file->uploaded != nullptr); + + file->uploaded->offset = data.offset; + + _scanUpdated.fire(collectScanInfo(*file)); + } +} + +void FormController::scanUploadFail(const FullMsgId &fullId) { + if (const auto file = findEditFile(fullId)) { + Assert(file->uploaded != nullptr); + + file->uploaded->offset = -1; + + _scanUpdated.fire(collectScanInfo(*file)); } } @@ -376,11 +454,17 @@ void FormController::editField(int index) { } } -void FormController::loadFiles(const std::vector &files) { - for (const auto &file : files) { +void FormController::loadFiles(std::vector &files) { + for (auto &file : files) { + if (!file.image.isNull()) { + file.downloadOffset = file.size; + continue; + } + const auto key = FileKey{ file.id, file.dcId }; const auto i = _fileLoaders.find(key); if (i == _fileLoaders.end()) { + file.downloadOffset = 0; const auto [i, ok] = _fileLoaders.emplace( key, std::make_unique( @@ -397,35 +481,91 @@ void FormController::loadFiles(const std::vector &files) { const auto loader = i->second.get(); loader->connect(loader, &mtpFileLoader::progress, [=] { if (loader->finished()) { - fileLoaded(key, loader->bytes()); + fileLoadDone(key, loader->bytes()); + } else { + fileLoadProgress(key, loader->currentOffset()); } }); loader->connect(loader, &mtpFileLoader::failed, [=] { + fileLoadFail(key); }); loader->start(); } } } -void FormController::fileLoaded(FileKey key, const QByteArray &bytes) { +void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) { if (const auto [field, file] = findFile(key); file != nullptr) { const auto decrypted = DecryptData( bytes::make_span(bytes), file->fileHash, field->secret); - auto image = App::readImage(QByteArray::fromRawData( + file->downloadOffset = file->size; + file->image = App::readImage(QByteArray::fromRawData( reinterpret_cast(decrypted.data()), decrypted.size())); - if (!image.isNull()) { - _scanUpdated.fire({ - FileKey{ file->id, file->dcId }, - QString("loaded"), - std::move(image), - }); + if (const auto fileInEdit = findEditFile(key)) { + fileInEdit->fields.image = file->image; + fileInEdit->fields.downloadOffset = file->downloadOffset; + _scanUpdated.fire(collectScanInfo(*fileInEdit)); } } } +void FormController::fileLoadProgress(FileKey key, int offset) { + if (const auto [field, file] = findFile(key); file != nullptr) { + file->downloadOffset = offset; + if (const auto fileInEdit = findEditFile(key)) { + fileInEdit->fields.downloadOffset = file->downloadOffset; + _scanUpdated.fire(collectScanInfo(*fileInEdit)); + } + } +} + +void FormController::fileLoadFail(FileKey key) { + if (const auto [field, file] = findFile(key); file != nullptr) { + file->downloadOffset = -1; + if (const auto fileInEdit = findEditFile(key)) { + fileInEdit->fields.downloadOffset = file->downloadOffset; + _scanUpdated.fire(collectScanInfo(*fileInEdit)); + } + } +} + +ScanInfo FormController::collectScanInfo(const EditFile &file) const { + const auto status = [&] { + if (file.deleted) { + return QString("deleted"); + } else if (file.fields.accessHash) { + if (file.fields.downloadOffset < 0) { + return QString("download failed"); + } else if (file.fields.downloadOffset < file.fields.size) { + return QString("downloading %1 / %2" + ).arg(file.fields.downloadOffset + ).arg(file.fields.size); + } else { + return QString("download ready"); + } + } else if (file.uploaded) { + if (file.uploaded->offset < 0) { + return QString("upload failed"); + } else if (file.uploaded->fullId) { + return QString("uploading %1 / %2" + ).arg(file.uploaded->offset + ).arg(file.uploaded->bytes.size()); + } else { + return QString("upload ready"); + } + } else { + return QString("preparing"); + } + }(); + return { + FileKey{ file.fields.id, file.fields.dcId }, + status, + file.fields.image }; +} + IdentityData FormController::fieldDataIdentity(const Field &field) const { const auto &map = field.parsedData; auto result = IdentityData(); @@ -442,10 +582,7 @@ std::vector FormController::fieldFilesIdentity( const Field &field) const { auto result = std::vector(); for (const auto &file : field.filesInEdit) { - result.push_back({ - FileKey{ file.fields.id, file.fields.dcId }, - langDateTime(QDateTime::currentDateTime()), - QImage() }); + result.push_back(collectScanInfo(file)); } return result; } @@ -481,7 +618,9 @@ void FormController::saveIdentity(int index) { auto inputFiles = QVector(); inputFiles.reserve(field.filesInEdit.size()); for (const auto &file : field.filesInEdit) { - if (const auto uploaded = file.uploaded.get()) { + if (file.deleted) { + continue; + } else if (const auto uploaded = file.uploaded.get()) { inputFiles.push_back(MTP_inputSecureFileUploaded( MTP_long(file.fields.id), MTP_int(uploaded->partsCount), @@ -501,7 +640,9 @@ void FormController::saveIdentity(int index) { field.secret); const auto fileHashes = ranges::view::all( field.filesInEdit - ) | ranges::view::transform([](const EditFile &file) { + ) | ranges::view::filter([](const EditFile &file) { + return !file.deleted; + }) | ranges::view::transform([](const EditFile &file) { return bytes::make_span(file.fields.fileHash); }); const auto valueHash = openssl::Sha256(bytes::concatenate( @@ -523,6 +664,13 @@ void FormController::saveIdentity(int index) { MTP_bytes(valueHash)), MTP_long(CountSecureSecretHash(_secret)) )).done([=](const MTPSecureValueSaved &result) { + Expects(result.type() == mtpc_secureValueSaved); + + const auto &data = result.c_secureValueSaved(); + _form.fields[index].files = parseFiles( + data.vfiles.v, + base::take(_form.fields[index].filesInEdit)); + Ui::show(Box("Saved"), LayerOption::KeepOther); }).fail([=](const RPCError &error) { Ui::show(Box("Error saving value.")); @@ -610,10 +758,22 @@ auto FormController::parseEncryptedField( const auto &fields = data.vdata.c_secureData(); result.originalData = fields.vdata.v; result.dataHash = bytes::make_vector(fields.vdata_hash.v); - for (const auto &file : data.vfiles.v) { + result.files = parseFiles(data.vfiles.v); + return result; +} + +auto FormController::parseFiles( + const QVector &data, + const std::vector &editData) const +-> std::vector { + auto result = std::vector(); + result.reserve(data.size()); + + auto index = 0; + for (const auto &file : data) { switch (file.type()) { case mtpc_secureFileEmpty: { - result.files.push_back(File()); + } break; case mtpc_secureFile: { const auto &fields = file.c_secureFile(); @@ -623,10 +783,20 @@ auto FormController::parseEncryptedField( normal.size = fields.vsize.v; normal.dcId = fields.vdc_id.v; normal.fileHash = bytes::make_vector(fields.vfile_hash.v); - result.files.push_back(std::move(normal)); + const auto i = ranges::find( + editData, + normal.fileHash, + [](const EditFile &file) { return file.fields.fileHash; }); + if (i != editData.end()) { + normal.image = i->fields.image; + normal.downloadOffset = i->fields.downloadOffset; + } + result.push_back(std::move(normal)); } break; } + ++index; } + return result; } @@ -705,6 +875,17 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* { return nullptr; } +auto FormController::findEditFile(const FileKey &key) -> EditFile* { + for (auto &field : _form.fields) { + for (auto &file : field.filesInEdit) { + if (file.fields.dcId == key.dcId && file.fields.id == key.id) { + return &file; + } + } + } + return nullptr; +} + auto FormController::findFile(const FileKey &key) -> std::pair { for (auto &field : _form.fields) { diff --git a/Telegram/SourceFiles/passport/passport_form_controller.h b/Telegram/SourceFiles/passport/passport_form_controller.h index cafa35229b..8f234bb43b 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_controller.h @@ -13,7 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class BoxContent; namespace Storage { -struct UploadedSecure; +struct UploadSecureDone; +struct UploadSecureProgress; } // namespace Storage namespace Window { @@ -65,7 +66,7 @@ struct FileKey { struct ScanInfo { FileKey key; - QString date; + QString status; QImage thumb; }; @@ -82,7 +83,8 @@ public: rpl::producer passwordError() const; QString passwordHint() const; - void uploadScan(int index, QByteArray &&content); + void uploadScan(int fieldIndex, QByteArray &&content); + void deleteScan(int fieldIndex, int fileIndex); rpl::producer<> secretReadyEvents() const; @@ -111,6 +113,8 @@ private: QByteArray md5checksum; bytes::vector hash; bytes::vector bytes; + + int offset = 0; }; struct File { uint64 id = 0; @@ -118,7 +122,9 @@ private: int32 size = 0; int32 dcId = 0; bytes::vector fileHash; - bytes::vector bytes; + + int downloadOffset = 0; + QImage image; }; struct EditFile { EditFile( @@ -127,6 +133,7 @@ private: File fields; std::unique_ptr uploaded; + bool deleted = false; }; struct Verification { TimeId date; @@ -170,6 +177,7 @@ private: }; EditFile *findEditFile(const FullMsgId &fullId); + EditFile *findEditFile(const FileKey &key); std::pair findFile(const FileKey &key); void requestForm(); @@ -190,6 +198,9 @@ private: const QByteArray &value, const DataType &data) const; Verification parseVerified(const MTPSecureValueVerified &data) const; + std::vector parseFiles( + const QVector &data, + const std::vector &editData = {}) const; void passwordDone(const MTPaccount_Password &result); void passwordFail(const RPCError &error); @@ -209,8 +220,10 @@ private: std::vector fieldFilesIdentity(const Field &field) const; void saveIdentity(int index); - void loadFiles(const std::vector &files); - void fileLoaded(FileKey key, const QByteArray &bytes); + void loadFiles(std::vector &files); + void fileLoadDone(FileKey key, const QByteArray &bytes); + void fileLoadProgress(FileKey key, int offset); + void fileLoadFail(FileKey key); void generateSecret(bytes::const_span password); template @@ -219,8 +232,15 @@ private: bytes::const_span valueHash); void subscribeToUploader(); - void uploadEncryptedScan(int index, UploadedScan &&data); - void scanUploaded(const Storage::UploadedSecure &data); + void encryptScan( + int fieldIndex, + int fileIndex, + QByteArray &&content); + void uploadEncryptedScan(int fieldIndex, int fileIndex, UploadedScan &&data); + void scanUploadDone(const Storage::UploadSecureDone &data); + void scanUploadProgress(const Storage::UploadSecureProgress &data); + void scanUploadFail(const FullMsgId &fullId); + ScanInfo collectScanInfo(const EditFile &file) const; not_null _controller; FormRequest _request; diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index eba0464f8f..4831e062f0 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -29,8 +29,8 @@ struct Uploader::File { std::shared_ptr file; SendMediaReady media; - int32 partsCount; - mutable int32 fileSentSize; + int32 partsCount = 0; + mutable int32 fileSentSize = 0; uint64 id() const; SendMediaType type() const; @@ -483,7 +483,11 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } _documentProgress.fire_copy(fullId); } else if (file.type() == SendMediaType::Secure) { - _secureProgress.fire_copy(fullId); + file.fileSentSize += sentPartSize; + _secureProgress.fire_copy({ + fullId, + file.fileSentSize, + file.file->partssize }); } } } diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 29e804fd86..040687dd05 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -31,7 +31,13 @@ struct UploadedThumbDocument { MTPInputFile thumb; }; -struct UploadedSecure { +struct UploadSecureProgress { + FullMsgId fullId; + int offset = 0; + int size = 0; +}; + +struct UploadSecureDone { FullMsgId fullId; uint64 fileId = 0; int partsCount = 0; @@ -65,7 +71,7 @@ public: rpl::producer thumbDocumentReady() const { return _thumbDocumentReady.events(); } - rpl::producer secureReady() const { + rpl::producer secureReady() const { return _secureReady.events(); } rpl::producer photoProgress() const { @@ -74,7 +80,7 @@ public: rpl::producer documentProgress() const { return _documentProgress.events(); } - rpl::producer secureProgress() const { + rpl::producer secureProgress() const { return _secureProgress.events(); } rpl::producer photoFailed() const { @@ -117,10 +123,10 @@ private: rpl::event_stream _photoReady; rpl::event_stream _documentReady; rpl::event_stream _thumbDocumentReady; - rpl::event_stream _secureReady; + rpl::event_stream _secureReady; rpl::event_stream _photoProgress; rpl::event_stream _documentProgress; - rpl::event_stream _secureProgress; + rpl::event_stream _secureProgress; rpl::event_stream _photoFailed; rpl::event_stream _documentFailed; rpl::event_stream _secureFailed;